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 we don't have an explicit id for this option, fall back to
123 id = opt->option->name;
125 return !strcmp(id, var);
128 static void append_text_to_current_arg(struct grub2_argv *argv,
129 const char *text, int len)
131 char *cur = argv->argv[argv->argc - 1];
134 int curlen = strlen(cur);
135 cur = talloc_realloc(argv->argv, cur, char, len + curlen + 1);
136 memcpy(cur + curlen, text, len);
137 cur[len + curlen] = '\0';
140 cur = talloc_strndup(argv->argv, text, len);
143 argv->argv[argv->argc-1] = cur;
146 /* Add a word to the argv array. Depending on the word type, and presence of
147 * delimiter characters, we may add multiple items to the array.
149 static void append_word_to_argv(struct grub2_script *script,
150 struct grub2_argv *argv, struct grub2_word *word)
152 const char *text, *pos;
155 /* If it's a variable, perform substitution */
156 if (word->type == GRUB2_WORD_VAR)
157 text = expand_var(script, word);
163 /* If we have no text, we leave the current word as-is. The caller
164 * has allocated an empty string for the case where this is the
165 * first text token */
169 /* If we're not splitting, we just add the entire block to the
170 * current argv item */
172 append_text_to_current_arg(argv, text, len);
176 /* Scan for delimiters. If we find a word-end boundary, add the word
177 * to the argv array, and start a new argv item */
178 pos = !is_delim(text[0]) ? text : NULL;
179 for (i = 0; i < len; i++) {
181 /* first delimiter after a word: add the accumulated word to
183 if (pos && is_delim(text[i])) {
184 append_text_to_current_arg(argv, pos,
188 /* first non-delimeter after a delimiter: record the starting
189 * position, and create another argv item */
190 } else if (!pos && !is_delim(text[i])) {
193 argv->argv = talloc_realloc(argv, argv->argv, char *,
195 argv->argv[argv->argc - 1] = NULL;
199 /* add remaining word characters */
201 append_text_to_current_arg(argv, pos, text + len - pos);
204 /* Transform an argv word-token list (returned from the parser) into an
205 * expanded argv array (as used by the script execution code). We do this by
206 * iterating through the words in an argv_list, looking for GRUB2_WORD_VAR
209 static void process_expansions(struct grub2_script *script,
210 struct grub2_argv *argv)
212 struct grub2_word *top_word, *word;
217 list_for_each_entry(&argv->words, top_word, argv_list) {
219 argv->argv = talloc_realloc(argv, argv->argv, char *,
221 /* because we've parsed a separate word here, we know that
222 * we need at least an empty string */
223 argv->argv[argv->argc - 1] = talloc_strdup(argv->argv, "");
225 for (word = top_word; word; word = word->next)
226 append_word_to_argv(script, argv, word);
229 /* we may have allocated an extra argv element but not populated it */
230 if (!argv->argv[argv->argc - 1])
234 static int statements_execute(struct grub2_script *script,
235 struct grub2_statements *stmts)
237 struct grub2_statement *stmt;
240 list_for_each_entry(&stmts->list, stmt, list) {
242 rc = stmt->exec(script, stmt);
247 int statement_simple_execute(struct grub2_script *script,
248 struct grub2_statement *statement)
250 struct grub2_statement_simple *st = to_stmt_simple(statement);
251 struct grub2_symtab_entry *entry;
258 process_expansions(script, st->argv);
263 /* is this a var=value assignment? */
264 pos = strchr(st->argv->argv[0], '=');
267 name = st->argv->argv[0];
268 name = talloc_strndup(st, name, pos - name);
270 script_env_set(script, name, value);
274 entry = script_lookup_function(script, st->argv->argv[0]);
276 pb_log("grub2: undefined function '%s'\n", st->argv->argv[0]);
280 rc = entry->fn(script, entry->data, st->argv->argc, st->argv->argv);
285 int statement_block_execute(struct grub2_script *script,
286 struct grub2_statement *statement)
288 struct grub2_statement_block *st = to_stmt_block(statement);
289 return statements_execute(script, st->statements);
292 /* returns 0 if the statement was executed, 1 otherwise */
293 static int statement_conditional_execute(struct grub2_script *script,
294 struct grub2_statement *statement, bool *executed)
296 struct grub2_statement_conditional *st = to_stmt_conditional(statement);
299 rc = st->condition->exec(script, st->condition);
302 rc = statements_execute(script, st->statements);
307 int statement_if_execute(struct grub2_script *script,
308 struct grub2_statement *statement)
310 struct grub2_statement_if *st = to_stmt_if(statement);
311 struct grub2_statement *conditional;
312 bool executed = false;
315 list_for_each_entry(&st->conditionals->list, conditional, list) {
316 rc = statement_conditional_execute(script,
317 conditional, &executed);
322 if (!executed && st->else_case)
323 rc = statements_execute(script, st->else_case);
328 int statement_menuentry_execute(struct grub2_script *script,
329 struct grub2_statement *statement)
331 struct grub2_statement_menuentry *st = to_stmt_menuentry(statement);
332 struct discover_boot_option *opt;
333 const char *id = NULL;
336 process_expansions(script, st->argv);
338 opt = discover_boot_option_create(script->ctx, script->ctx->device);
340 /* XXX: --options=values need to be parsed properly; this is a simple
341 * implementation to get --id= working.
343 for (i = 1; i < st->argv->argc; ++i) {
344 if (strncmp("--id=", st->argv->argv[i], 5) == 0) {
345 id = st->argv->argv[i] + 5;
349 if (st->argv->argc > 0)
350 opt->option->name = talloc_strdup(opt, st->argv->argv[0]);
352 opt->option->name = talloc_strdup(opt, "(unknown)");
354 opt->option->id = talloc_asprintf(opt->option, "%s#%s",
355 script->ctx->device->device->id,
356 id ? id : opt->option->name);
360 statements_execute(script, st->statements);
362 if (!opt->boot_image)
365 opt->option->is_default = option_is_default(script, opt, id);
367 list_add_tail(&script->options, &opt->list);
374 static int function_invoke(struct grub2_script *script,
375 void *data, int argc, char **argv)
377 struct grub2_statement_function *fn = data;
381 /* set positional parameters */
382 for (i = 1; i < argc; i++) {
383 name = talloc_asprintf(script, "%d", i);
384 script_env_set(script, name, argv[i]);
387 return statements_execute(script, fn->body);
390 int statement_function_execute(struct grub2_script *script,
391 struct grub2_statement *statement)
393 struct grub2_statement_function *st = to_stmt_function(statement);
396 if (st->name->type == GRUB2_WORD_VAR)
397 name = expand_var(script, st->name);
399 name = st->name->text;
401 script_register_function(script, name, function_invoke, st);
406 int statement_for_execute(struct grub2_script *script,
407 struct grub2_statement *statement)
409 struct grub2_statement_for *st = to_stmt_for(statement);
413 if (st->var->type == GRUB2_WORD_VAR)
414 expand_var(script, st->var);
415 varname = st->var->text;
417 process_expansions(script, st->list);
419 for (i = 0; i < st->list->argc; ++i) {
420 script_env_set(script, varname, st->list->argv[i]);
421 rc = statements_execute(script, st->body);
427 static void init_env(struct grub2_script *script)
429 struct env_entry *env;
432 list_init(&script->environment);
434 /* use location of the parsed config file to determine the prefix */
435 env = talloc(script, struct env_entry);
438 if (script->filename) {
439 sep = strrchr(script->filename, '/');
441 prefix = talloc_strndup(env, script->filename,
442 sep - script->filename);
445 script_env_set(script, "prefix", prefix ? : default_prefix);
449 /* establish feature settings */
450 script_env_set(script, "feature_menuentry_id", "y");
453 void script_register_function(struct grub2_script *script,
454 const char *name, grub2_function fn,
457 struct grub2_symtab_entry *entry;
459 entry = talloc(script, struct grub2_symtab_entry);
463 list_add(&script->symtab, &entry->list);
466 static void set_fallback_default(struct grub2_script *script)
468 struct discover_boot_option *opt, *first = NULL;
469 bool have_default = false;
471 list_for_each_entry(&script->options, opt, list) {
474 have_default = have_default || opt->option->is_default;
477 if (!have_default && first) {
478 const char *env = script_env_get(script, "default");
480 pb_log("grub: no explicit default (env default=%s), "
481 "falling back to first option (%s)\n",
482 env ?: "unset", first->option->name);
484 first->option->is_default = true;
488 void script_execute(struct grub2_script *script)
490 struct discover_boot_option *opt, *tmp;
493 statements_execute(script, script->statements);
495 set_fallback_default(script);
497 list_for_each_entry_safe(&script->options, opt, tmp, list)
498 discover_context_add_boot_option(script->ctx, opt);
500 /* Our option list will be invalid, as we've added all options to the
501 * discover context */
502 list_init(&script->options);
505 struct grub2_script *create_script(struct grub2_parser *parser,
506 struct discover_context *ctx)
508 struct grub2_script *script;
510 script = talloc_zero(parser, struct grub2_script);
514 list_init(&script->symtab);
515 list_init(&script->options);
516 register_builtins(script);