]> git.ozlabs.org Git - petitboot/commitdiff
discover/grub2: Allow unset and invalid defaults
authorJeremy Kerr <jk@ozlabs.org>
Thu, 11 Dec 2014 01:42:58 +0000 (09:42 +0800)
committerJeremy Kerr <jk@ozlabs.org>
Thu, 11 Dec 2014 07:11:42 +0000 (15:11 +0800)
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 <jk@ozlabs.org>
discover/grub2/grub2.h
discover/grub2/script.c
test/parser/Makefile.am
test/parser/test-grub2-implicit-default-invalid.c [new file with mode: 0644]
test/parser/test-grub2-implicit-default-unset.c [new file with mode: 0644]

index 0a8932426588b1a8e90b5f575a6ffcc542e94eba..68176fbe3e18cca5f4ed010204812b7312f7b423 100644 (file)
@@ -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 {
index 36f73b21abfc4a0992a5a8a5540382d6102581c1..f52168157cfb03aa3e3eb49e5ff3fd00039e66f2 100644 (file)
@@ -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;
index 1bb45e8fa11b1cc7bdb796f687affbb02d3f1e67..d69ca7f83f1299b81400d719a068824f4d176b36 100644 (file)
@@ -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 (file)
index 0000000..72902db
--- /dev/null
@@ -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 (file)
index 0000000..ecc4c00
--- /dev/null
@@ -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);
+}