X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=blobdiff_plain;f=discover%2Fgrub2%2Fscript.c;h=c4f58d92bacb27395647a23d9c4f9feb5d8d6016;hp=9c087373ac0454524308fa6da5370a0b86b69cc5;hb=d6b11766acb4457457419a84f460a3ce7a8f4693;hpb=e8a50ad2461a8efaa4d71ea19692a1b63a0f9bc2 diff --git a/discover/grub2/script.c b/discover/grub2/script.c index 9c08737..c4f58d9 100644 --- a/discover/grub2/script.c +++ b/discover/grub2/script.c @@ -1,7 +1,9 @@ #include #include +#include +#include #include #include @@ -9,16 +11,20 @@ #define to_stmt_simple(stmt) \ container_of(stmt, struct grub2_statement_simple, st) +#define to_stmt_block(stmt) \ + container_of(stmt, struct grub2_statement_block, st) #define to_stmt_if(stmt) \ container_of(stmt, struct grub2_statement_if, st) #define to_stmt_menuentry(stmt) \ container_of(stmt, struct grub2_statement_menuentry, st) #define to_stmt_function(stmt) \ container_of(stmt, struct grub2_statement_function, st) +#define to_stmt_conditional(stmt) \ + container_of(stmt, struct grub2_statement_conditional, st) struct env_entry { - const char *name; - const char *value; + char *name; + char *value; struct list_item list; }; @@ -29,6 +35,8 @@ struct grub2_symtab_entry { struct list_item list; }; +static const char *default_prefix = "/boot/grub"; + static struct grub2_symtab_entry *script_lookup_function( struct grub2_script *script, const char *name) { @@ -67,11 +75,13 @@ void script_env_set(struct grub2_script *script, if (!entry) { entry = talloc(script, struct env_entry); - entry->name = name; + entry->name = talloc_strdup(entry, name); list_add(&script->environment, &entry->list); + } else { + talloc_free(entry->value); } - entry->value = value; + entry->value = talloc_strdup(entry, value); } static bool expand_var(struct grub2_script *script, struct grub2_word *word) @@ -92,6 +102,29 @@ static bool is_delim(char c) return c == ' ' || c == '\t'; } +static bool option_is_default(struct grub2_script *script, + struct discover_boot_option *opt, const char *id) +{ + unsigned int default_idx; + const char *var; + char *end; + + var = script_env_get(script, "default"); + if (!var) + return false; + + default_idx = strtoul(var, &end, 10); + if (end != var && *end == '\0') + return default_idx == script->n_options; + + /* if we don't have an explicit id for this option, fall back to + * the name */ + if (!id) + id = opt->option->name; + + return !strcmp(id, var); +} + /* For non-double-quoted variable expansions, we may need to split the * variable's value into multiple argv items. * @@ -231,6 +264,7 @@ int statement_simple_execute(struct grub2_script *script, { struct grub2_statement_simple *st = to_stmt_simple(statement); struct grub2_symtab_entry *entry; + char *pos; int rc; if (!st->argv) @@ -241,10 +275,21 @@ int statement_simple_execute(struct grub2_script *script, if (!st->argv->argc) return 0; + /* is this a var=value assignment? */ + pos = strchr(st->argv->argv[0], '='); + if (pos) { + char *name, *value; + name = st->argv->argv[0]; + name = talloc_strndup(st, name, pos - name); + value = pos + 1; + script_env_set(script, name, value); + return 0; + } + entry = script_lookup_function(script, st->argv->argv[0]); if (!entry) { - fprintf(stderr, "undefined function '%s'\n", st->argv->argv[0]); - return 0; + pb_log("grub2: undefined function '%s'\n", st->argv->argv[0]); + return 1; } rc = entry->fn(script, entry->data, st->argv->argc, st->argv->argv); @@ -252,24 +297,45 @@ int statement_simple_execute(struct grub2_script *script, return rc; } -int statement_if_execute(struct grub2_script *script, +int statement_block_execute(struct grub2_script *script, struct grub2_statement *statement) { - struct grub2_statement_if *st = to_stmt_if(statement); - struct grub2_statements *case_stmts; + struct grub2_statement_block *st = to_stmt_block(statement); + return statements_execute(script, st->statements); +} + +/* returns 0 if the statement was executed, 1 otherwise */ +static int statement_conditional_execute(struct grub2_script *script, + struct grub2_statement *statement, bool *executed) +{ + struct grub2_statement_conditional *st = to_stmt_conditional(statement); int rc; rc = st->condition->exec(script, st->condition); + *executed = (!rc); + if (*executed) + rc = statements_execute(script, st->statements); - if (rc == 0) - case_stmts = st->true_case; - else - case_stmts = st->false_case; + return rc; +} - if (case_stmts) - statements_execute(script, case_stmts); - else - rc = 0; +int statement_if_execute(struct grub2_script *script, + struct grub2_statement *statement) +{ + struct grub2_statement_if *st = to_stmt_if(statement); + struct grub2_statement *conditional; + bool executed; + int rc = 0; + + list_for_each_entry(&st->conditionals->list, conditional, list) { + rc = statement_conditional_execute(script, + conditional, &executed); + if (executed) + break; + } + + if (!executed && st->else_case) + rc = statements_execute(script, st->else_case); return rc; } @@ -279,21 +345,39 @@ int statement_menuentry_execute(struct grub2_script *script, { struct grub2_statement_menuentry *st = to_stmt_menuentry(statement); struct discover_boot_option *opt; + const char *id = NULL; + int i; process_expansions(script, st->argv); opt = discover_boot_option_create(script->ctx, script->ctx->device); - if (st->argv->argc > 0) { + + /* XXX: --options=values need to be parsed properly; this is a simple + * implementation to get --id= working. + */ + for (i = 1; i < st->argv->argc; ++i) { + if (strncmp("--id=", st->argv->argv[i], 5) == 0) { + id = st->argv->argv[i] + 5; + break; + } + } + if (st->argv->argc > 0) opt->option->name = talloc_strdup(opt, st->argv->argv[0]); - } else { + else opt->option->name = talloc_strdup(opt, "(unknown)"); - } + + opt->option->id = talloc_asprintf(opt->option, "%s#%s", + script->ctx->device->device->id, + id ? id : opt->option->name); script->opt = opt; statements_execute(script, st->statements); + opt->option->is_default = option_is_default(script, opt, id); + discover_context_add_boot_option(script->ctx, opt); + script->n_options++; script->opt = NULL; return 0; @@ -333,14 +417,24 @@ int statement_function_execute(struct grub2_script *script, static void init_env(struct grub2_script *script) { struct env_entry *env; + char *prefix, *sep; list_init(&script->environment); + /* use location of the parsed config file to determine the prefix */ env = talloc(script, struct env_entry); - env->name = talloc_strdup(env, "prefix"); - env->value = talloc_strdup(env, "/"); - list_add(&script->environment, &env->list); + prefix = NULL; + if (script->filename) { + sep = strrchr(script->filename, '/'); + if (sep) + prefix = talloc_strndup(env, script->filename, + sep - script->filename); + } + + script_env_set(script, "prefix", prefix ? : default_prefix); + if (prefix) + talloc_free(prefix); } void script_register_function(struct grub2_script *script, @@ -359,6 +453,7 @@ void script_register_function(struct grub2_script *script, void script_execute(struct grub2_script *script) { + init_env(script); statements_execute(script, script->statements); } @@ -367,11 +462,9 @@ struct grub2_script *create_script(struct grub2_parser *parser, { struct grub2_script *script; - script = talloc(parser, struct grub2_script); + script = talloc_zero(parser, struct grub2_script); - init_env(script); script->ctx = ctx; - script->opt = NULL; list_init(&script->symtab); register_builtins(script);