From e330e3f5adf78d3ba77217995a4bc3cd1fd16f4c Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Thu, 11 Dec 2014 09:42:58 +0800 Subject: discover/grub2: Allow unset and invalid defaults If the default environment variable is unset or invalid (i.e., references a non-existent boot option), then GRUB2 will fallback to the first boot option present. This is preventing petitboot from autobooting where no default is explicitly set, or is stale. This change adds this fallback behaviour to petitboot. Because we don't know if the first option will be a default at parse time (as no other options matched the default env var), we need to keep options in a list, and register them with the discover server once the parse is complete. Signed-off-by: Jeremy Kerr --- discover/grub2/grub2.h | 1 + discover/grub2/script.c | 35 ++++++++++++++++++++++- test/parser/Makefile.am | 2 ++ test/parser/test-grub2-implicit-default-invalid.c | 26 +++++++++++++++++ test/parser/test-grub2-implicit-default-unset.c | 25 ++++++++++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 test/parser/test-grub2-implicit-default-invalid.c create mode 100644 test/parser/test-grub2-implicit-default-unset.c diff --git a/discover/grub2/grub2.h b/discover/grub2/grub2.h index 0a89324..68176fb 100644 --- a/discover/grub2/grub2.h +++ b/discover/grub2/grub2.h @@ -98,6 +98,7 @@ struct grub2_script { struct discover_boot_option *opt; const char *filename; unsigned int n_options; + struct list options; }; struct grub2_parser { diff --git a/discover/grub2/script.c b/discover/grub2/script.c index 36f73b2..f521681 100644 --- a/discover/grub2/script.c +++ b/discover/grub2/script.c @@ -364,7 +364,7 @@ int statement_menuentry_execute(struct grub2_script *script, opt->option->is_default = option_is_default(script, opt, id); - discover_context_add_boot_option(script->ctx, opt); + list_add_tail(&script->options, &opt->list); script->n_options++; script->opt = NULL; @@ -463,11 +463,43 @@ void script_register_function(struct grub2_script *script, list_add(&script->symtab, &entry->list); } +static void set_fallback_default(struct grub2_script *script) +{ + struct discover_boot_option *opt, *first = NULL; + bool have_default = false; + + list_for_each_entry(&script->options, opt, list) { + if (!first) + first = opt; + have_default = have_default || opt->option->is_default; + } + + if (!have_default && first) { + const char *env = script_env_get(script, "default"); + + pb_log("grub: no explicit default (env default=%s), " + "falling back to first option (%s)\n", + env ?: "unset", first->option->name); + + first->option->is_default = true; + } +} void script_execute(struct grub2_script *script) { + struct discover_boot_option *opt, *tmp; + init_env(script); statements_execute(script, script->statements); + + set_fallback_default(script); + + list_for_each_entry_safe(&script->options, opt, tmp, list) + discover_context_add_boot_option(script->ctx, opt); + + /* Our option list will be invalid, as we've added all options to the + * discover context */ + list_init(&script->options); } struct grub2_script *create_script(struct grub2_parser *parser, @@ -480,6 +512,7 @@ struct grub2_script *create_script(struct grub2_parser *parser, script->ctx = ctx; list_init(&script->symtab); + list_init(&script->options); register_builtins(script); return script; diff --git a/test/parser/Makefile.am b/test/parser/Makefile.am index 1bb45e8..d69ca7f 100644 --- a/test/parser/Makefile.am +++ b/test/parser/Makefile.am @@ -21,6 +21,8 @@ parser_TESTS = \ test/parser/test-grub2-if-formats \ test/parser/test-grub2-default-index \ test/parser/test-grub2-default-multiword \ + test/parser/test-grub2-implicit-default-unset \ + test/parser/test-grub2-implicit-default-invalid \ test/parser/test-grub2-multiple-resolve \ test/parser/test-grub2-multiple-id \ test/parser/test-grub2-single-line-if \ diff --git a/test/parser/test-grub2-implicit-default-invalid.c b/test/parser/test-grub2-implicit-default-invalid.c new file mode 100644 index 0000000..72902db --- /dev/null +++ b/test/parser/test-grub2-implicit-default-invalid.c @@ -0,0 +1,26 @@ + +#include "parser-test.h" + +#if 0 /* PARSER_EMBEDDED_CONFIG */ +default=missing +menuentry 'test.1' { + linux /vmlinux +} +menuentry 'test.2' { + linux /vmlinux +} +#endif + +void run_test(struct parser_test *test) +{ + struct discover_boot_option *opt; + + test_read_conf_embedded(test, "/grub2/grub.cfg"); + test_run_parser(test, "grub2"); + + check_boot_option_count(test->ctx, 2); + opt = get_boot_option(test->ctx, 0); + + check_name(opt, "test.1"); + check_is_default(opt); +} diff --git a/test/parser/test-grub2-implicit-default-unset.c b/test/parser/test-grub2-implicit-default-unset.c new file mode 100644 index 0000000..ecc4c00 --- /dev/null +++ b/test/parser/test-grub2-implicit-default-unset.c @@ -0,0 +1,25 @@ + +#include "parser-test.h" + +#if 0 /* PARSER_EMBEDDED_CONFIG */ +menuentry 'test.1' { + linux /vmlinux +} +menuentry 'test.2' { + linux /vmlinux +} +#endif + +void run_test(struct parser_test *test) +{ + struct discover_boot_option *opt; + + test_read_conf_embedded(test, "/grub2/grub.cfg"); + test_run_parser(test, "grub2"); + + check_boot_option_count(test->ctx, 2); + opt = get_boot_option(test->ctx, 0); + + check_name(opt, "test.1"); + check_is_default(opt); +} -- cgit v1.2.1