summaryrefslogtreecommitdiffstats
path: root/discover/grub2
diff options
context:
space:
mode:
Diffstat (limited to 'discover/grub2')
-rw-r--r--discover/grub2/blscfg.c19
-rw-r--r--discover/grub2/builtins.c208
-rw-r--r--discover/grub2/env.c2
-rw-r--r--discover/grub2/grub2-parser.y17
-rw-r--r--discover/grub2/grub2.c110
-rw-r--r--discover/grub2/grub2.h32
-rw-r--r--discover/grub2/script.c29
7 files changed, 337 insertions, 80 deletions
diff --git a/discover/grub2/blscfg.c b/discover/grub2/blscfg.c
index d4754aa..d08f8f0 100644
--- a/discover/grub2/blscfg.c
+++ b/discover/grub2/blscfg.c
@@ -166,7 +166,6 @@ static void bls_finish(struct conf_context *conf)
struct discover_context *dc = conf->dc;
struct discover_boot_option *opt = state->opt;
struct boot_option *option = opt->option;
- const char *root;
char *filename;
if (!state->image) {
@@ -192,23 +191,21 @@ static void bls_finish(struct conf_context *conf)
else
option->name = talloc_strdup(option, state->image);
- root = script_env_get(state->script, "root");
-
- opt->boot_image = create_grub2_resource(opt, conf->dc->device,
- root, state->image);
+ opt->boot_image = create_grub2_resource(state->script, opt,
+ state->image);
if (state->initrd)
- opt->initrd = create_grub2_resource(opt, conf->dc->device,
- root, state->initrd);
+ opt->initrd = create_grub2_resource(state->script, opt,
+ state->initrd);
if (state->dtb)
- opt->dtb = create_grub2_resource(opt, conf->dc->device,
- root, state->dtb);
+ opt->dtb = create_grub2_resource(state->script, opt,
+ state->dtb);
char* args_sigfile_default = talloc_asprintf(opt,
"%s.cmdline.sig", state->image);
- opt->args_sig_file = create_grub2_resource(opt, conf->dc->device,
- root, args_sigfile_default);
+ opt->args_sig_file = create_grub2_resource(state->script, opt,
+ args_sigfile_default);
talloc_free(args_sigfile_default);
option->is_default = option_is_default(state, option);
diff --git a/discover/grub2/builtins.c b/discover/grub2/builtins.c
index 7e92299..ab1407a 100644
--- a/discover/grub2/builtins.c
+++ b/discover/grub2/builtins.c
@@ -1,4 +1,7 @@
+#define _GNU_SOURCE
+
+#include <getopt.h>
#include <stdio.h>
#include <string.h>
@@ -43,7 +46,6 @@ static int builtin_linux(struct grub2_script *script,
int argc, char *argv[])
{
struct discover_boot_option *opt = script->opt;
- const char *root;
int i;
if (!opt) {
@@ -58,10 +60,7 @@ static int builtin_linux(struct grub2_script *script,
return -1;
}
- root = script_env_get(script, "root");
-
- opt->boot_image = create_grub2_resource(opt, script->ctx->device,
- root, argv[1]);
+ opt->boot_image = create_grub2_resource(script, opt, argv[1]);
opt->option->boot_args = NULL;
if (argc > 2)
@@ -74,8 +73,8 @@ static int builtin_linux(struct grub2_script *script,
char* args_sigfile_default = talloc_asprintf(opt,
"%s.cmdline.sig", argv[1]);
- opt->args_sig_file = create_grub2_resource(opt, script->ctx->device,
- root, args_sigfile_default);
+ opt->args_sig_file = create_grub2_resource(script, opt,
+ args_sigfile_default);
talloc_free(args_sigfile_default);
return 0;
}
@@ -85,7 +84,6 @@ static int builtin_initrd(struct grub2_script *script,
int argc, char *argv[])
{
struct discover_boot_option *opt = script->opt;
- const char *root;
if (!opt) {
pb_log("grub2 syntax error: 'initrd' statement outside "
@@ -99,35 +97,128 @@ static int builtin_initrd(struct grub2_script *script,
return -1;
}
- root = script_env_get(script, "root");
- opt->initrd = create_grub2_resource(opt, script->ctx->device,
- root, argv[1]);
+ opt->initrd = create_grub2_resource(script, opt, argv[1]);
return 0;
}
+static const struct option search_options[] = {
+ {
+ .name = "set",
+ .has_arg = required_argument,
+ .val = 's',
+ },
+ {
+ .name = "file",
+ .has_arg = no_argument,
+ .val = 'f',
+ },
+ {
+ .name = "label",
+ .has_arg = no_argument,
+ .val = 'l',
+ },
+ {
+ .name = "fs-uuid",
+ .has_arg = no_argument,
+ .val = 'u',
+ },
+ { 0 },
+};
+
static int builtin_search(struct grub2_script *script,
void *data __attribute__((unused)),
int argc, char *argv[])
{
- const char *env_var, *spec;
- int i;
-
- env_var = NULL;
+ const char *env_var, *spec, *res;
+ struct discover_device *dev;
+ enum {
+ LOOKUP_UUID = 'u',
+ LOOKUP_LABEL = 'l',
+ LOOKUP_FILE = 'f',
+ } lookup_type;
+
+ env_var = "root";
+ optind = 0;
+
+ /* Default to UUID, for backwards compat with earlier petitboot
+ * versions. This argument is non-optional in GRUB. */
+ lookup_type = LOOKUP_UUID;
+
+ for (;;) {
+ int c = getopt_long(argc, argv, ":flu", search_options, NULL);
+ if (c == -1)
+ break;
- for (i = 1; i < argc - 1; i++) {
- if (!strncmp(argv[i], "--set=", strlen("--set="))) {
- env_var = argv[i] + strlen("--set=");
+ switch (c) {
+ case 's':
+ env_var = optarg;
+ break;
+ case LOOKUP_UUID:
+ case LOOKUP_LABEL:
+ case LOOKUP_FILE:
+ lookup_type = c;
+ break;
+ case '?':
+ case ':':
break;
}
}
- if (!env_var)
+ if (!strlen(env_var))
return 0;
- spec = argv[argc - 1];
+ if (optind >= argc)
+ return -1;
+
+ spec = argv[optind];
+ res = NULL;
+
+ switch (lookup_type) {
+ case LOOKUP_UUID:
+ dev = device_lookup_by_uuid(script->ctx->handler,
+ spec);
+ res = dev ? dev->device->id : spec;
+ break;
+ case LOOKUP_LABEL:
+ dev = device_lookup_by_label(script->ctx->handler,
+ spec);
+ if (dev)
+ res = dev->device->id;
+ break;
+ case LOOKUP_FILE:
+ /* not yet implemented */
+ break;
+ }
+
+ if (res)
+ script_env_set(script, env_var, res);
+
+ return 0;
+}
+
+static int parse_to_device_path(struct grub2_script *script,
+ const char *desc, struct discover_device **devp,
+ char **pathp)
+{
+ struct discover_device *dev;
+ struct grub2_file *file;
+
+ file = grub2_parse_file(script, desc);
+ if (!file)
+ return -1;
+
+ dev = script->ctx->device;
+ if (file->dev)
+ dev = grub2_lookup_device(script->ctx->handler, file->dev);
+
+ if (!dev)
+ return -1;
+
+ *devp = dev;
+ *pathp = talloc_strdup(script, file->path);
- script_env_set(script, env_var, spec);
+ talloc_free(file);
return 0;
}
@@ -139,12 +230,17 @@ static int builtin_search(struct grub2_script *script,
static bool builtin_test_op_file(struct grub2_script *script, char op,
const char *file)
{
+ struct discover_device *dev;
+ struct stat statbuf;
bool result;
+ char *path;
int rc;
- struct stat statbuf;
- rc = parser_stat_path(script->ctx, script->ctx->device,
- file, &statbuf);
+ rc = parse_to_device_path(script, file, &dev, &path);
+ if (rc)
+ return false;
+
+ rc = parser_stat_path(script->ctx, dev, path, &statbuf);
if (rc)
return false;
@@ -172,16 +268,21 @@ static bool builtin_test_op_file(struct grub2_script *script, char op,
static bool builtin_test_op_dir(struct grub2_script *script, char op,
const char *dir)
{
- int rc;
+ struct discover_device *dev;
struct stat statbuf;
+ char *path;
+ int rc;
if (op != 'd')
return false;
- rc = parser_stat_path(script->ctx, script->ctx->device, dir, &statbuf);
- if (rc) {
+ rc = parse_to_device_path(script, dir, &dev, &path);
+ if (rc)
+ return false;
+
+ rc = parser_stat_path(script->ctx, dev, path, &statbuf);
+ if (rc)
return false;
- }
return S_ISDIR(statbuf.st_mode);
}
@@ -300,6 +401,51 @@ static int builtin_test(struct grub2_script *script,
return rc ? 0 : 1;
}
+static int builtin_source(struct grub2_script *script,
+ void *data __attribute__((unused)),
+ int argc, char *argv[])
+{
+ struct grub2_statements *statements;
+ struct discover_device *dev;
+ const char *filename;
+ char *path, *buf;
+ int rc, len;
+
+ if (argc != 2)
+ return false;
+
+ /* limit script recursion */
+ if (script->include_depth >= 10)
+ return false;
+
+ rc = parse_to_device_path(script, argv[1], &dev, &path);
+ if (rc)
+ return false;
+
+ rc = parser_request_file(script->ctx, dev, path, &buf, &len);
+ if (rc)
+ return false;
+
+ /* save current script state */
+ statements = script->statements;
+ filename = script->filename;
+ script->include_depth++;
+
+ rc = grub2_parser_parse(script->parser, argv[1], buf, len);
+
+ if (!rc)
+ statements_execute(script, script->statements);
+
+ talloc_free(script->statements);
+
+ /* restore state */
+ script->statements = statements;
+ script->filename = filename;
+ script->include_depth--;
+
+ return !rc;
+}
+
static int builtin_true(struct grub2_script *script __attribute__((unused)),
void *data __attribute__((unused)),
int argc __attribute__((unused)),
@@ -390,7 +536,11 @@ static struct {
{
.name = "blscfg",
.fn = builtin_blscfg,
- }
+ },
+ {
+ .name = "source",
+ .fn = builtin_source,
+ },
};
static const char *nops[] = {
diff --git a/discover/grub2/env.c b/discover/grub2/env.c
index 7eda095..74d5729 100644
--- a/discover/grub2/env.c
+++ b/discover/grub2/env.c
@@ -86,6 +86,8 @@ int builtin_load_env(struct grub2_script *script,
if (!rc) {
rc = parse_buf_to_env(script, buf, len);
+ if (rc)
+ pb_debug_fn("Failed to set env\n");
talloc_free(buf);
}
diff --git a/discover/grub2/grub2-parser.y b/discover/grub2/grub2-parser.y
index 527a61c..f99bbfd 100644
--- a/discover/grub2/grub2-parser.y
+++ b/discover/grub2/grub2-parser.y
@@ -331,14 +331,15 @@ struct grub2_parser *grub2_parser_create(struct discover_context *ctx)
return parser;
}
-void grub2_parser_parse(struct grub2_parser *parser, const char *filename,
+/* performs a parse on buf, setting parser->script->statements */
+int grub2_parser_parse(struct grub2_parser *parser, const char *filename,
char *buf, int len)
{
YY_BUFFER_STATE bufstate;
int rc;
if (!len)
- return;
+ return -1;
parser->script->filename = filename;
@@ -349,6 +350,18 @@ void grub2_parser_parse(struct grub2_parser *parser, const char *filename,
yy_delete_buffer(bufstate, parser->scanner);
+ parser->inter_word = false;
+
+ return rc;
+}
+
+void grub2_parser_parse_and_execute(struct grub2_parser *parser,
+ const char *filename, char *buf, int len)
+{
+ int rc;
+
+ rc = grub2_parser_parse(parser, filename, buf, len);
+
if (!rc)
script_execute(parser->script);
}
diff --git a/discover/grub2/grub2.c b/discover/grub2/grub2.c
index f62ccdd..b176ce2 100644
--- a/discover/grub2/grub2.c
+++ b/discover/grub2/grub2.c
@@ -33,18 +33,35 @@ static const char *const grub2_conf_files[] = {
NULL
};
-struct grub2_resource_info {
- char *root;
- char *path;
-};
+struct discover_device *grub2_lookup_device(struct device_handler *handler,
+ const char *desc)
+{
+ struct discover_device *dev;
+
+ if (!desc || !*desc)
+ return NULL;
+
+ dev = device_lookup_by_id(handler, desc);
+ if (dev)
+ return dev;
+
+ /* for now, only lookup by UUID */
+ dev = device_lookup_by_uuid(handler, desc);
+ if (dev)
+ return dev;
+
+ return NULL;
+}
/* we use slightly different resources for grub2 */
-struct resource *create_grub2_resource(struct discover_boot_option *opt,
- struct discover_device *orig_device,
- const char *root, const char *path)
+struct resource *create_grub2_resource(struct grub2_script *script,
+ struct discover_boot_option *opt,
+ const char *path)
{
- struct grub2_resource_info *info;
+ struct discover_device *dev;
+ struct grub2_file *file;
struct resource *res;
+ const char *root;
if (strstr(path, "://")) {
struct pb_url *url = pb_url_parse(opt, path);
@@ -52,19 +69,29 @@ struct resource *create_grub2_resource(struct discover_boot_option *opt,
return create_url_resource(opt, url);
}
+ file = grub2_parse_file(script, path);
+ if (!file)
+ return NULL;
+
res = talloc(opt, struct resource);
+ root = script_env_get(script, "root");
- if (root) {
- info = talloc(res, struct grub2_resource_info);
- talloc_reference(info, root);
- info->root = talloc_strdup(info, root);
- info->path = talloc_strdup(info, path);
+ if (!file->dev && root && strlen(root))
+ file->dev = talloc_strdup(file, root);
- res->resolved = false;
- res->info = info;
+ /* if we don't have a device specified, or the lookup succeeds now,
+ * then we can resolve the resource right away */
+ if (file->dev)
+ dev = grub2_lookup_device(script->ctx->handler, file->dev);
+ else
+ dev = script->ctx->device;
- } else
- resolve_resource_against_device(res, orig_device, path);
+ if (dev) {
+ resolve_resource_against_device(res, dev, file->path);
+ } else {
+ res->resolved = false;
+ res->info = talloc_steal(opt, file);
+ }
return res;
}
@@ -72,22 +99,59 @@ struct resource *create_grub2_resource(struct discover_boot_option *opt,
bool resolve_grub2_resource(struct device_handler *handler,
struct resource *res)
{
- struct grub2_resource_info *info = res->info;
+ struct grub2_file *file = res->info;
struct discover_device *dev;
assert(!res->resolved);
- dev = device_lookup_by_uuid(handler, info->root);
-
+ dev = grub2_lookup_device(handler, file->dev);
if (!dev)
return false;
- resolve_resource_against_device(res, dev, info->path);
- talloc_free(info);
+ resolve_resource_against_device(res, dev, file->path);
+ talloc_free(file);
return true;
}
+struct grub2_file *grub2_parse_file(struct grub2_script *script,
+ const char *str)
+{
+ struct grub2_file *file;
+ size_t dev_len;
+ char *pos;
+
+ if (!str)
+ return NULL;
+
+ file = talloc_zero(script, struct grub2_file);
+
+ if (*str != '(') {
+ /* just a path - no device, return path as-is */
+ file->path = talloc_strdup(file, str);
+
+ } else {
+ /* device plus path - split into components */
+
+ pos = strchr(str, ')');
+
+ /* no closing bracket, or zero-length path? */
+ if (!pos || *(pos+1) == '\0') {
+ talloc_free(file);
+ return NULL;
+ }
+
+ file->path = talloc_strdup(file, pos + 1);
+
+ dev_len = pos - str - 1;
+ if (dev_len)
+ file->dev = talloc_strndup(file, str + 1, dev_len);
+ }
+
+ return file;
+}
+
+
static int grub2_parse(struct discover_context *dc)
{
const char * const *filename;
@@ -105,7 +169,7 @@ static int grub2_parse(struct discover_context *dc)
continue;
parser = grub2_parser_create(dc);
- grub2_parser_parse(parser, *filename, buf, len);
+ grub2_parser_parse_and_execute(parser, *filename, buf, len);
device_handler_status_dev_info(dc->handler, dc->device,
_("Parsed GRUB configuration from %s"),
*filename);
diff --git a/discover/grub2/grub2.h b/discover/grub2/grub2.h
index 68176fb..75f6aa0 100644
--- a/discover/grub2/grub2.h
+++ b/discover/grub2/grub2.h
@@ -91,6 +91,7 @@ struct grub2_statement_for {
};
struct grub2_script {
+ struct grub2_parser *parser;
struct grub2_statements *statements;
struct list environment;
struct list symtab;
@@ -99,6 +100,7 @@ struct grub2_script {
const char *filename;
unsigned int n_options;
struct list options;
+ int include_depth;
};
struct grub2_parser {
@@ -107,6 +109,14 @@ struct grub2_parser {
bool inter_word;
};
+/* References to files in grub2 consist of an optional device and a path
+ * (specified here by UUID). If the dev is unspecified, we fall back to a
+ * default - usually the 'root' environment variable. */
+struct grub2_file {
+ char *dev;
+ char *path;
+};
+
/* type for builtin functions */
typedef int (*grub2_function)(struct grub2_script *script, void *data,
int argc, char *argv[]);
@@ -156,6 +166,9 @@ void word_append(struct grub2_word *w1, struct grub2_word *w2);
/* script interface */
void script_execute(struct grub2_script *script);
+int statements_execute(struct grub2_script *script,
+ struct grub2_statements *stmts);
+
int statement_simple_execute(struct grub2_script *script,
struct grub2_statement *statement);
int statement_block_execute(struct grub2_script *script,
@@ -183,16 +196,25 @@ void script_register_function(struct grub2_script *script,
void register_builtins(struct grub2_script *script);
/* resources */
-struct resource *create_grub2_resource(struct discover_boot_option *opt,
- struct discover_device *orig_device,
- const char *root, const char *path);
+struct resource *create_grub2_resource(struct grub2_script *script,
+ struct discover_boot_option *opt, const char *path);
bool resolve_grub2_resource(struct device_handler *handler,
struct resource *res);
+/* grub-style device+path parsing */
+struct grub2_file *grub2_parse_file(struct grub2_script *script,
+ const char *str);
+struct discover_device *grub2_lookup_device(struct device_handler *handler,
+ const char *desc);
+
+/* internal parse api */
+int grub2_parser_parse(struct grub2_parser *parser, const char *filename,
+ char *buf, int len);
+
/* external parser api */
struct grub2_parser *grub2_parser_create(struct discover_context *ctx);
-void grub2_parser_parse(struct grub2_parser *parser, const char *filename,
- char *buf, int len);
+void grub2_parser_parse_and_execute(struct grub2_parser *parser,
+ const char *filename, char *buf, int len);
#endif /* GRUB2_H */
diff --git a/discover/grub2/script.c b/discover/grub2/script.c
index 1a802b9..14931f9 100644
--- a/discover/grub2/script.c
+++ b/discover/grub2/script.c
@@ -117,12 +117,10 @@ static bool option_is_default(struct grub2_script *script,
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;
+ if (id && !strcmp(id, var))
+ return true;
- return !strcmp(id, var);
+ return !strcmp(opt->option->name, var);
}
static void append_text_to_current_arg(struct grub2_argv *argv,
@@ -227,11 +225,11 @@ static void process_expansions(struct grub2_script *script,
}
/* we may have allocated an extra argv element but not populated it */
- if (!argv->argv[argv->argc - 1])
+ if (argv->argv && !argv->argv[argv->argc - 1])
argv->argc--;
}
-static int statements_execute(struct grub2_script *script,
+int statements_execute(struct grub2_script *script,
struct grub2_statements *stmts)
{
struct grub2_statement *stmt;
@@ -341,9 +339,16 @@ int statement_menuentry_execute(struct grub2_script *script,
* 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 (strncmp("--id", st->argv->argv[i], strlen("--id")) == 0) {
+ if (strlen(st->argv->argv[i]) > strlen("--id=")) {
+ id = st->argv->argv[i] + strlen("--id=");
+ break;
+ }
+
+ if (i + 1 < st->argv->argc) {
+ id = st->argv->argv[i + 1];
+ break;
+ }
}
}
if (st->argv->argc > 0)
@@ -489,6 +494,9 @@ void script_execute(struct grub2_script *script)
{
struct discover_boot_option *opt, *tmp;
+ if (!script)
+ return;
+
init_env(script);
statements_execute(script, script->statements);
@@ -510,6 +518,7 @@ struct grub2_script *create_script(struct grub2_parser *parser,
script = talloc_zero(parser, struct grub2_script);
script->ctx = ctx;
+ script->parser = parser;
list_init(&script->symtab);
list_init(&script->options);
OpenPOWER on IntegriCloud