diff options
author | Javier Martinez Canillas <javierm@redhat.com> | 2018-03-22 09:41:23 +0100 |
---|---|---|
committer | Samuel Mendoza-Jonas <sam@mendozajonas.com> | 2018-03-23 11:39:35 +1100 |
commit | 91ce1a8f8863d8f740188236f138421d17292d6c (patch) | |
tree | f39fa6f339e8c8f3e3ec20052acc33b806aa040b /discover/grub2/blscfg.c | |
parent | 3dfa4123bdf987aaa0e4bfd73d436c6bab0184ce (diff) | |
download | talos-petitboot-91ce1a8f8863d8f740188236f138421d17292d6c.tar.gz talos-petitboot-91ce1a8f8863d8f740188236f138421d17292d6c.zip |
discover/grub: Add blscfg command support to parse BootLoaderSpec files
The BootLoaderSpec (BLS) defines a file format for boot configurations,
so bootloaders can parse these files and create their boot menu entries
by using the information provided by them [0].
This allow to configure the boot items as drop-in files in a directory
instead of having to parse and modify a bootloader configuration file.
The GRUB 2 bootloader provides a blscfg command that parses these files
and creates menu entries using this information. Add support for it.
[0]: https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
Diffstat (limited to 'discover/grub2/blscfg.c')
-rw-r--r-- | discover/grub2/blscfg.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/discover/grub2/blscfg.c b/discover/grub2/blscfg.c new file mode 100644 index 0000000..0f69f7e --- /dev/null +++ b/discover/grub2/blscfg.c @@ -0,0 +1,241 @@ + +#define _GNU_SOURCE + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> + +#include <log/log.h> +#include <file/file.h> +#include <talloc/talloc.h> +#include <i18n/i18n.h> + +#include "grub2.h" +#include "discover/parser-conf.h" +#include "discover/parser.h" + +#define BLS_DIR "/loader/entries" + +struct bls_state { + struct discover_boot_option *opt; + struct grub2_script *script; + const char *filename; + const char *title; + const char *version; + const char *machine_id; + const char *image; + const char *initrd; + const char *dtb; +}; + +static void bls_process_pair(struct conf_context *conf, const char *name, + char *value) +{ + struct bls_state *state = conf->parser_info; + struct discover_boot_option *opt = state->opt; + struct boot_option *option = opt->option; + const char *boot_args; + + if (streq(name, "title")) { + state->title = talloc_strdup(state, value); + return; + } + + if (streq(name, "version")) { + state->version = talloc_strdup(state, value); + return; + } + + if (streq(name, "machine-id")) { + state->machine_id = talloc_strdup(state, value); + return; + } + + if (streq(name, "linux")) { + state->image = talloc_strdup(state, value); + return; + } + + if (streq(name, "initrd")) { + state->initrd = talloc_strdup(state, value); + return; + } + + if (streq(name, "devicetree")) { + state->dtb = talloc_strdup(state, value); + return; + } + + if (streq(name, "options")) { + if (value[0] == '$') { + boot_args = script_env_get(state->script, value + 1); + if (!boot_args) + return; + + option->boot_args = talloc_strdup(opt, boot_args); + } else { + option->boot_args = talloc_strdup(opt, value); + } + return; + } +} + +static bool option_is_default(struct grub2_script *script, + struct boot_option *option) +{ + const char *var; + + var = script_env_get(script, "default"); + if (!var) + return false; + + if (!strcmp(var, option->id)) + return true; + + return !strcmp(var, option->name); +} + +static void bls_finish(struct conf_context *conf) +{ + struct bls_state *state = conf->parser_info; + 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) { + device_handler_status_dev_info(dc->handler, dc->device, + _("linux field not found in %s"), + state->filename); + return; + } + + filename = basename(state->filename); + filename[strlen(filename) - strlen(".conf")] = '\0'; + + option->id = talloc_strdup(option, filename); + + if (state->title) + option->name = talloc_strdup(option, state->title); + else if (state->machine_id && state->version) + option->name = talloc_asprintf(option, "%s %s", + state->machine_id, + state->version); + else if (state->version) + option->name = talloc_strdup(option, state->version); + 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); + + if (state->initrd) + opt->initrd = create_grub2_resource(opt, conf->dc->device, + root, state->initrd); + + if (state->dtb) + opt->dtb = create_grub2_resource(opt, conf->dc->device, + root, state->dtb); + + option->is_default = option_is_default(state->script, option); + + discover_context_add_boot_option(dc, opt); + + device_handler_status_dev_info(dc->handler, dc->device, + _("Created menu entry from BLS file %s"), + state->filename); +} + +static int bls_filter(const struct dirent *ent) +{ + int offset = strlen(ent->d_name) - strlen(".conf"); + + if (offset < 0) + return 0; + + return strncmp(ent->d_name + offset, ".conf", strlen(".conf")) == 0; +} + +static int bls_sort(const struct dirent **ent_a, const struct dirent **ent_b) +{ + return strverscmp((*ent_b)->d_name, (*ent_a)->d_name); +} + +int builtin_blscfg(struct grub2_script *script, + void *data __attribute__((unused)), + int argc __attribute__((unused)), + char *argv[] __attribute__((unused))); + +int builtin_blscfg(struct grub2_script *script, + void *data __attribute__((unused)), + int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) +{ + struct discover_context *dc = script->ctx; + struct dirent **bls_entries; + struct conf_context *conf; + struct bls_state *state; + char *buf, *filename; + int n, len, rc = -1; + + conf = talloc_zero(dc, struct conf_context); + if (!conf) + return rc; + + conf->dc = dc; + conf->get_pair = conf_get_pair_space; + conf->process_pair = bls_process_pair; + conf->finish = bls_finish; + + n = parser_scandir(dc, BLS_DIR, &bls_entries, bls_filter, bls_sort); + if (n <= 0) + goto err; + + while (n--) { + filename = talloc_asprintf(dc, BLS_DIR"/%s", + bls_entries[n]->d_name); + if (!filename) + break; + + state = talloc_zero(conf, struct bls_state); + if (!state) + break; + + state->opt = discover_boot_option_create(dc, dc->device); + if (!state->opt) + break; + + state->script = script; + state->filename = filename; + conf->parser_info = state; + + rc = parser_request_file(dc, dc->device, filename, &buf, &len); + if (rc) + break; + + conf_parse_buf(conf, buf, len); + + talloc_free(buf); + talloc_free(state); + talloc_free(filename); + free(bls_entries[n]); + } + + if (n > 0) { + device_handler_status_dev_info(dc->handler, dc->device, + _("Scanning %s failed"), + BLS_DIR); + do { + free(bls_entries[n]); + } while (n-- > 0); + } + + free(bls_entries); +err: + talloc_free(conf); + return rc; +} |