7 #include <types/types.h>
8 #include <talloc/talloc.h>
12 #define to_stmt_simple(stmt) \
13 container_of(stmt, struct grub2_statement_simple, st)
14 #define to_stmt_block(stmt) \
15 container_of(stmt, struct grub2_statement_block, st)
16 #define to_stmt_if(stmt) \
17 container_of(stmt, struct grub2_statement_if, st)
18 #define to_stmt_menuentry(stmt) \
19 container_of(stmt, struct grub2_statement_menuentry, st)
20 #define to_stmt_function(stmt) \
21 container_of(stmt, struct grub2_statement_function, st)
22 #define to_stmt_for(stmt) \
23 container_of(stmt, struct grub2_statement_for, st)
24 #define to_stmt_conditional(stmt) \
25 container_of(stmt, struct grub2_statement_conditional, st)
30 struct list_item list;
33 struct grub2_symtab_entry {
37 struct list_item list;
40 static const char *default_prefix = "/boot/grub";
42 static struct grub2_symtab_entry *script_lookup_function(
43 struct grub2_script *script, const char *name)
45 struct grub2_symtab_entry *entry;
47 list_for_each_entry(&script->symtab, entry, list) {
48 if (!strcmp(entry->name, name))
55 const char *script_env_get(struct grub2_script *script, const char *name)
57 struct env_entry *entry;
59 list_for_each_entry(&script->environment, entry, list)
60 if (!strcmp(entry->name, name))
66 void script_env_set(struct grub2_script *script,
67 const char *name, const char *value)
69 struct env_entry *e, *entry = NULL;
71 list_for_each_entry(&script->environment, e, list) {
72 if (!strcmp(e->name, name)) {
79 entry = talloc(script, struct env_entry);
80 entry->name = talloc_strdup(entry, name);
81 list_add(&script->environment, &entry->list);
83 talloc_free(entry->value);
86 entry->value = talloc_strdup(entry, value);
89 static char *expand_var(struct grub2_script *script, struct grub2_word *word)
93 val = script_env_get(script, word->name);
97 return talloc_strdup(script, val);
100 static bool is_delim(char c)
102 return c == ' ' || c == '\t';
105 static bool option_is_default(struct grub2_script *script,
106 struct discover_boot_option *opt, const char *id)
108 unsigned int default_idx;
112 var = script_env_get(script, "default");
116 default_idx = strtoul(var, &end, 10);
117 if (end != var && *end == '\0')
118 return default_idx == script->n_options;
120 if (id && !strcmp(id, var))
123 return !strcmp(opt->option->name, var);
126 static void append_text_to_current_arg(struct grub2_argv *argv,
127 const char *text, int len)
129 char *cur = argv->argv[argv->argc - 1];
132 int curlen = strlen(cur);
133 cur = talloc_realloc(argv->argv, cur, char, len + curlen + 1);
134 memcpy(cur + curlen, text, len);
135 cur[len + curlen] = '\0';
138 cur = talloc_strndup(argv->argv, text, len);
141 argv->argv[argv->argc-1] = cur;
144 /* Add a word to the argv array. Depending on the word type, and presence of
145 * delimiter characters, we may add multiple items to the array.
147 static void append_word_to_argv(struct grub2_script *script,
148 struct grub2_argv *argv, struct grub2_word *word)
150 const char *text, *pos;
153 /* If it's a variable, perform substitution */
154 if (word->type == GRUB2_WORD_VAR)
155 text = expand_var(script, word);
161 /* If we have no text, we leave the current word as-is. The caller
162 * has allocated an empty string for the case where this is the
163 * first text token */
167 /* If we're not splitting, we just add the entire block to the
168 * current argv item */
170 append_text_to_current_arg(argv, text, len);
174 /* Scan for delimiters. If we find a word-end boundary, add the word
175 * to the argv array, and start a new argv item */
176 pos = !is_delim(text[0]) ? text : NULL;
177 for (i = 0; i < len; i++) {
179 /* first delimiter after a word: add the accumulated word to
181 if (pos && is_delim(text[i])) {
182 append_text_to_current_arg(argv, pos,
186 /* first non-delimeter after a delimiter: record the starting
187 * position, and create another argv item */
188 } else if (!pos && !is_delim(text[i])) {
191 argv->argv = talloc_realloc(argv, argv->argv, char *,
193 argv->argv[argv->argc - 1] = NULL;
197 /* add remaining word characters */
199 append_text_to_current_arg(argv, pos, text + len - pos);
202 /* Transform an argv word-token list (returned from the parser) into an
203 * expanded argv array (as used by the script execution code). We do this by
204 * iterating through the words in an argv_list, looking for GRUB2_WORD_VAR
207 static void process_expansions(struct grub2_script *script,
208 struct grub2_argv *argv)
210 struct grub2_word *top_word, *word;
215 list_for_each_entry(&argv->words, top_word, argv_list) {
217 argv->argv = talloc_realloc(argv, argv->argv, char *,
219 /* because we've parsed a separate word here, we know that
220 * we need at least an empty string */
221 argv->argv[argv->argc - 1] = talloc_strdup(argv->argv, "");
223 for (word = top_word; word; word = word->next)
224 append_word_to_argv(script, argv, word);
227 /* we may have allocated an extra argv element but not populated it */
228 if (argv->argv && !argv->argv[argv->argc - 1])
232 static int statements_execute(struct grub2_script *script,
233 struct grub2_statements *stmts)
235 struct grub2_statement *stmt;
238 list_for_each_entry(&stmts->list, stmt, list) {
240 rc = stmt->exec(script, stmt);
245 int statement_simple_execute(struct grub2_script *script,
246 struct grub2_statement *statement)
248 struct grub2_statement_simple *st = to_stmt_simple(statement);
249 struct grub2_symtab_entry *entry;
256 process_expansions(script, st->argv);
261 /* is this a var=value assignment? */
262 pos = strchr(st->argv->argv[0], '=');
265 name = st->argv->argv[0];
266 name = talloc_strndup(st, name, pos - name);
268 script_env_set(script, name, value);
272 entry = script_lookup_function(script, st->argv->argv[0]);
274 pb_log("grub2: undefined function '%s'\n", st->argv->argv[0]);
278 rc = entry->fn(script, entry->data, st->argv->argc, st->argv->argv);
283 int statement_block_execute(struct grub2_script *script,
284 struct grub2_statement *statement)
286 struct grub2_statement_block *st = to_stmt_block(statement);
287 return statements_execute(script, st->statements);
290 /* returns 0 if the statement was executed, 1 otherwise */
291 static int statement_conditional_execute(struct grub2_script *script,
292 struct grub2_statement *statement, bool *executed)
294 struct grub2_statement_conditional *st = to_stmt_conditional(statement);
297 rc = st->condition->exec(script, st->condition);
300 rc = statements_execute(script, st->statements);
305 int statement_if_execute(struct grub2_script *script,
306 struct grub2_statement *statement)
308 struct grub2_statement_if *st = to_stmt_if(statement);
309 struct grub2_statement *conditional;
310 bool executed = false;
313 list_for_each_entry(&st->conditionals->list, conditional, list) {
314 rc = statement_conditional_execute(script,
315 conditional, &executed);
320 if (!executed && st->else_case)
321 rc = statements_execute(script, st->else_case);
326 int statement_menuentry_execute(struct grub2_script *script,
327 struct grub2_statement *statement)
329 struct grub2_statement_menuentry *st = to_stmt_menuentry(statement);
330 struct discover_boot_option *opt;
331 const char *id = NULL;
334 process_expansions(script, st->argv);
336 opt = discover_boot_option_create(script->ctx, script->ctx->device);
338 /* XXX: --options=values need to be parsed properly; this is a simple
339 * implementation to get --id= working.
341 for (i = 1; i < st->argv->argc; ++i) {
342 if (strncmp("--id=", st->argv->argv[i], 5) == 0) {
343 id = st->argv->argv[i] + 5;
347 if (st->argv->argc > 0)
348 opt->option->name = talloc_strdup(opt, st->argv->argv[0]);
350 opt->option->name = talloc_strdup(opt, "(unknown)");
352 opt->option->id = talloc_asprintf(opt->option, "%s#%s",
353 script->ctx->device->device->id,
354 id ? id : opt->option->name);
358 statements_execute(script, st->statements);
360 if (!opt->boot_image)
363 opt->option->is_default = option_is_default(script, opt, id);
365 list_add_tail(&script->options, &opt->list);
372 static int function_invoke(struct grub2_script *script,
373 void *data, int argc, char **argv)
375 struct grub2_statement_function *fn = data;
379 /* set positional parameters */
380 for (i = 1; i < argc; i++) {
381 name = talloc_asprintf(script, "%d", i);
382 script_env_set(script, name, argv[i]);
385 return statements_execute(script, fn->body);
388 int statement_function_execute(struct grub2_script *script,
389 struct grub2_statement *statement)
391 struct grub2_statement_function *st = to_stmt_function(statement);
394 if (st->name->type == GRUB2_WORD_VAR)
395 name = expand_var(script, st->name);
397 name = st->name->text;
399 script_register_function(script, name, function_invoke, st);
404 int statement_for_execute(struct grub2_script *script,
405 struct grub2_statement *statement)
407 struct grub2_statement_for *st = to_stmt_for(statement);
411 if (st->var->type == GRUB2_WORD_VAR)
412 expand_var(script, st->var);
413 varname = st->var->text;
415 process_expansions(script, st->list);
417 for (i = 0; i < st->list->argc; ++i) {
418 script_env_set(script, varname, st->list->argv[i]);
419 rc = statements_execute(script, st->body);
425 static void init_env(struct grub2_script *script)
427 struct env_entry *env;
430 list_init(&script->environment);
432 /* use location of the parsed config file to determine the prefix */
433 env = talloc(script, struct env_entry);
436 if (script->filename) {
437 sep = strrchr(script->filename, '/');
439 prefix = talloc_strndup(env, script->filename,
440 sep - script->filename);
443 script_env_set(script, "prefix", prefix ? : default_prefix);
447 /* establish feature settings */
448 script_env_set(script, "feature_menuentry_id", "y");
451 void script_register_function(struct grub2_script *script,
452 const char *name, grub2_function fn,
455 struct grub2_symtab_entry *entry;
457 entry = talloc(script, struct grub2_symtab_entry);
461 list_add(&script->symtab, &entry->list);
464 static void set_fallback_default(struct grub2_script *script)
466 struct discover_boot_option *opt, *first = NULL;
467 bool have_default = false;
469 list_for_each_entry(&script->options, opt, list) {
472 have_default = have_default || opt->option->is_default;
475 if (!have_default && first) {
476 const char *env = script_env_get(script, "default");
478 pb_log("grub: no explicit default (env default=%s), "
479 "falling back to first option (%s)\n",
480 env ?: "unset", first->option->name);
482 first->option->is_default = true;
486 void script_execute(struct grub2_script *script)
488 struct discover_boot_option *opt, *tmp;
494 statements_execute(script, script->statements);
496 set_fallback_default(script);
498 list_for_each_entry_safe(&script->options, opt, tmp, list)
499 discover_context_add_boot_option(script->ctx, opt);
501 /* Our option list will be invalid, as we've added all options to the
502 * discover context */
503 list_init(&script->options);
506 struct grub2_script *create_script(struct grub2_parser *parser,
507 struct discover_context *ctx)
509 struct grub2_script *script;
511 script = talloc_zero(parser, struct grub2_script);
515 list_init(&script->symtab);
516 list_init(&script->options);
517 register_builtins(script);