grub2/0018-blscfg-add-blscfg-module-to-parse-Boot-Loader-Specif.patch
Javier Martinez Canillas 1d49572ef1
Update to latest content from upstream sources
The content of this branch was not automatically imported from upstream
sources. Pull the latest from upstream to have the missing changes here.

Source: https://src.fedoraproject.org/rpms/grub2.git#f2763e56df79eccae17d2e8fa13d2f51a0fe7073

Resolves: rhbz#1947696

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
2021-04-12 01:36:21 +02:00

1610 lines
39 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Tue, 22 Jan 2013 06:31:38 +0100
Subject: [PATCH] blscfg: add blscfg module to parse Boot Loader Specification
snippets
The BootLoaderSpec (BLS) defines a scheme where different bootloaders can
share a format for boot items and a configuration directory that accepts
these common configurations as drop-in files.
Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
[wjt: some cleanups and fixes]
Signed-off-by: Will Thompson <wjt@endlessm.com>
---
grub-core/Makefile.core.def | 11 +
grub-core/commands/blscfg.c | 1177 ++++++++++++++++++++++++++++++++++++++++
grub-core/commands/legacycfg.c | 5 +-
grub-core/commands/loadenv.c | 77 +--
grub-core/commands/menuentry.c | 20 +-
grub-core/normal/main.c | 6 +
grub-core/commands/loadenv.h | 93 ++++
include/grub/compiler.h | 2 +
include/grub/menu.h | 13 +
include/grub/normal.h | 2 +-
10 files changed, 1324 insertions(+), 82 deletions(-)
create mode 100644 grub-core/commands/blscfg.c
create mode 100644 grub-core/commands/loadenv.h
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index c865a08b027..c15e91943b9 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -814,6 +814,16 @@ module = {
common = commands/blocklist.c;
};
+module = {
+ name = blscfg;
+ common = commands/blscfg.c;
+ common = commands/loadenv.h;
+ enable = powerpc_ieee1275;
+ enable = efi;
+ enable = i386_pc;
+ enable = emu;
+};
+
module = {
name = boot;
common = commands/boot.c;
@@ -980,6 +990,7 @@ module = {
module = {
name = loadenv;
common = commands/loadenv.c;
+ common = commands/loadenv.h;
common = lib/envblk.c;
};
diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c
new file mode 100644
index 00000000000..e907a6a5d28
--- /dev/null
+++ b/grub-core/commands/blscfg.c
@@ -0,0 +1,1177 @@
+/*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/
+
+/* bls.c - implementation of the boot loader spec */
+
+/*
+ * GRUB -- GRand Unified Bootloader
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/list.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/fs.h>
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/lib/envblk.h>
+
+#include <stdbool.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#include "loadenv.h"
+
+#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
+#ifdef GRUB_MACHINE_EMU
+#define GRUB_BOOT_DEVICE "/boot"
+#else
+#define GRUB_BOOT_DEVICE "($root)"
+#endif
+
+struct keyval
+{
+ const char *key;
+ char *val;
+};
+
+static struct bls_entry *entries = NULL;
+
+#define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
+
+static int bls_add_keyval(struct bls_entry *entry, char *key, char *val)
+{
+ char *k, *v;
+ struct keyval **kvs, *kv;
+ int new_n = entry->nkeyvals + 1;
+
+ kvs = grub_realloc (entry->keyvals, new_n * sizeof (struct keyval *));
+ if (!kvs)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't find space for BLS entry");
+ entry->keyvals = kvs;
+
+ kv = grub_malloc (sizeof (struct keyval));
+ if (!kv)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't find space for BLS entry");
+
+ k = grub_strdup (key);
+ if (!k)
+ {
+ grub_free (kv);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't find space for BLS entry");
+ }
+
+ v = grub_strdup (val);
+ if (!v)
+ {
+ grub_free (k);
+ grub_free (kv);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't find space for BLS entry");
+ }
+
+ kv->key = k;
+ kv->val = v;
+
+ entry->keyvals[entry->nkeyvals] = kv;
+ grub_dprintf("blscfg", "new keyval at %p:%s:%s\n", entry->keyvals[entry->nkeyvals], k, v);
+ entry->nkeyvals = new_n;
+
+ return 0;
+}
+
+/* Find they value of the key named by keyname. If there are allowed to be
+ * more than one, pass a pointer to an int set to -1 the first time, and pass
+ * the same pointer through each time after, and it'll return them in sorted
+ * order as defined in the BLS fragment file */
+static char *bls_get_val(struct bls_entry *entry, const char *keyname, int *last)
+{
+ int idx, start = 0;
+ struct keyval *kv = NULL;
+
+ if (last)
+ start = *last + 1;
+
+ for (idx = start; idx < entry->nkeyvals; idx++) {
+ kv = entry->keyvals[idx];
+
+ if (!grub_strcmp (keyname, kv->key))
+ break;
+ }
+
+ if (idx == entry->nkeyvals) {
+ if (last)
+ *last = -1;
+ return NULL;
+ }
+
+ if (last)
+ *last = idx;
+
+ return kv->val;
+}
+
+#define goto_return(x) ({ ret = (x); goto finish; })
+
+/* compare alpha and numeric segments of two versions */
+/* return 1: a is newer than b */
+/* 0: a and b are the same version */
+/* -1: b is newer than a */
+static int vercmp(const char * a, const char * b)
+{
+ char oldch1, oldch2;
+ char *abuf, *bbuf;
+ char *str1, *str2;
+ char * one, * two;
+ int rc;
+ int isnum;
+ int ret = 0;
+
+ grub_dprintf("blscfg", "%s comparing %s and %s\n", __func__, a, b);
+ if (!grub_strcmp(a, b))
+ return 0;
+
+ abuf = grub_malloc(grub_strlen(a) + 1);
+ bbuf = grub_malloc(grub_strlen(b) + 1);
+ str1 = abuf;
+ str2 = bbuf;
+ grub_strcpy(str1, a);
+ grub_strcpy(str2, b);
+
+ one = str1;
+ two = str2;
+
+ /* loop through each version segment of str1 and str2 and compare them */
+ while (*one || *two) {
+ while (*one && !grub_isalnum(*one) && *one != '~' && *one != '+') one++;
+ while (*two && !grub_isalnum(*two) && *two != '~' && *two != '+') two++;
+
+ /* handle the tilde separator, it sorts before everything else */
+ if (*one == '~' || *two == '~') {
+ if (*one != '~') goto_return (1);
+ if (*two != '~') goto_return (-1);
+ one++;
+ two++;
+ continue;
+ }
+
+ /*
+ * Handle plus separator. Concept is the same as tilde,
+ * except that if one of the strings ends (base version),
+ * the other is considered as higher version.
+ */
+ if (*one == '+' || *two == '+') {
+ if (!*one) return -1;
+ if (!*two) return 1;
+ if (*one != '+') goto_return (1);
+ if (*two != '+') goto_return (-1);
+ one++;
+ two++;
+ continue;
+ }
+
+ /* If we ran to the end of either, we are finished with the loop */
+ if (!(*one && *two)) break;
+
+ str1 = one;
+ str2 = two;
+
+ /* grab first completely alpha or completely numeric segment */
+ /* leave one and two pointing to the start of the alpha or numeric */
+ /* segment and walk str1 and str2 to end of segment */
+ if (grub_isdigit(*str1)) {
+ while (*str1 && grub_isdigit(*str1)) str1++;
+ while (*str2 && grub_isdigit(*str2)) str2++;
+ isnum = 1;
+ } else {
+ while (*str1 && grub_isalpha(*str1)) str1++;
+ while (*str2 && grub_isalpha(*str2)) str2++;
+ isnum = 0;
+ }
+
+ /* save character at the end of the alpha or numeric segment */
+ /* so that they can be restored after the comparison */
+ oldch1 = *str1;
+ *str1 = '\0';
+ oldch2 = *str2;
+ *str2 = '\0';
+
+ /* this cannot happen, as we previously tested to make sure that */
+ /* the first string has a non-null segment */
+ if (one == str1) goto_return(-1); /* arbitrary */
+
+ /* take care of the case where the two version segments are */
+ /* different types: one numeric, the other alpha (i.e. empty) */
+ /* numeric segments are always newer than alpha segments */
+ /* XXX See patch #60884 (and details) from bugzilla #50977. */
+ if (two == str2) goto_return (isnum ? 1 : -1);
+
+ if (isnum) {
+ grub_size_t onelen, twolen;
+ /* this used to be done by converting the digit segments */
+ /* to ints using atoi() - it's changed because long */
+ /* digit segments can overflow an int - this should fix that. */
+
+ /* throw away any leading zeros - it's a number, right? */
+ while (*one == '0') one++;
+ while (*two == '0') two++;
+
+ /* whichever number has more digits wins */
+ onelen = grub_strlen(one);
+ twolen = grub_strlen(two);
+ if (onelen > twolen) goto_return (1);
+ if (twolen > onelen) goto_return (-1);
+ }
+
+ /* grub_strcmp will return which one is greater - even if the two */
+ /* segments are alpha or if they are numeric. don't return */
+ /* if they are equal because there might be more segments to */
+ /* compare */
+ rc = grub_strcmp(one, two);
+ if (rc) goto_return (rc < 1 ? -1 : 1);
+
+ /* restore character that was replaced by null above */
+ *str1 = oldch1;
+ one = str1;
+ *str2 = oldch2;
+ two = str2;
+ }
+
+ /* this catches the case where all numeric and alpha segments have */
+ /* compared identically but the segment sepparating characters were */
+ /* different */
+ if ((!*one) && (!*two)) goto_return (0);
+
+ /* whichever version still has characters left over wins */
+ if (!*one) goto_return (-1); else goto_return (1);
+
+finish:
+ grub_free (abuf);
+ grub_free (bbuf);
+ return ret;
+}
+
+/* returns name/version/release */
+/* NULL string pointer returned if nothing found */
+static void
+split_package_string (char *package_string, char **name,
+ char **version, char **release)
+{
+ char *package_version, *package_release;
+
+ /* Release */
+ package_release = grub_strrchr (package_string, '-');
+
+ if (package_release != NULL)
+ *package_release++ = '\0';
+
+ *release = package_release;
+
+ if (name == NULL)
+ {
+ *version = package_string;
+ }
+ else
+ {
+ /* Version */
+ package_version = grub_strrchr(package_string, '-');
+
+ if (package_version != NULL)
+ *package_version++ = '\0';
+
+ *version = package_version;
+ /* Name */
+ *name = package_string;
+ }
+
+ /* Bubble up non-null values from release to name */
+ if (name != NULL && *name == NULL)
+ {
+ *name = (*version == NULL ? *release : *version);
+ *version = *release;
+ *release = NULL;
+ }
+ if (*version == NULL)
+ {
+ *version = *release;
+ *release = NULL;
+ }
+}
+
+static int
+split_cmp(char *nvr0, char *nvr1, int has_name)
+{
+ int ret = 0;
+ char *name0, *version0, *release0;
+ char *name1, *version1, *release1;
+
+ split_package_string(nvr0, has_name ? &name0 : NULL, &version0, &release0);
+ split_package_string(nvr1, has_name ? &name1 : NULL, &version1, &release1);
+
+ if (has_name)
+ {
+ ret = vercmp(name0 == NULL ? "" : name0,
+ name1 == NULL ? "" : name1);
+ if (ret != 0)
+ return ret;
+ }
+
+ ret = vercmp(version0 == NULL ? "" : version0,
+ version1 == NULL ? "" : version1);
+ if (ret != 0)
+ return ret;
+
+ ret = vercmp(release0 == NULL ? "" : release0,
+ release1 == NULL ? "" : release1);
+ return ret;
+}
+
+/* return 1: e0 is newer than e1 */
+/* 0: e0 and e1 are the same version */
+/* -1: e1 is newer than e0 */
+static int bls_cmp(const struct bls_entry *e0, const struct bls_entry *e1)
+{
+ char *id0, *id1;
+ int r;
+
+ id0 = grub_strdup(e0->filename);
+ id1 = grub_strdup(e1->filename);
+
+ r = split_cmp(id0, id1, 1);
+
+ grub_free(id0);
+ grub_free(id1);
+
+ return r;
+}
+
+static void list_add_tail(struct bls_entry *head, struct bls_entry *item)
+{
+ item->next = head;
+ if (head->prev)
+ head->prev->next = item;
+ item->prev = head->prev;
+ head->prev = item;
+}
+
+static int bls_add_entry(struct bls_entry *entry)
+{
+ struct bls_entry *e, *last = NULL;
+ int rc;
+
+ if (!entries) {
+ grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
+ entries = entry;
+ return 0;
+ }
+
+ FOR_BLS_ENTRIES(e) {
+ rc = bls_cmp(entry, e);
+
+ if (!rc)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (rc == 1) {
+ grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
+ list_add_tail (e, entry);
+ if (e == entries) {
+ entries = entry;
+ entry->prev = NULL;
+ }
+ return 0;
+ }
+ last = e;
+ }
+
+ if (last) {
+ grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
+ last->next = entry;
+ entry->prev = last;
+ }
+
+ return 0;
+}
+
+struct read_entry_info {
+ const char *devid;
+ const char *dirname;
+ grub_file_t file;
+};
+
+static int read_entry (
+ const char *filename,
+ const struct grub_dirhook_info *dirhook_info UNUSED,
+ void *data)
+{
+ grub_size_t m = 0, n, clip = 0;
+ int rc = 0;
+ char *p = NULL;
+ grub_file_t f = NULL;
+ struct bls_entry *entry;
+ struct read_entry_info *info = (struct read_entry_info *)data;
+
+ grub_dprintf ("blscfg", "filename: \"%s\"\n", filename);
+
+ n = grub_strlen (filename);
+
+ if (info->file)
+ {
+ f = info->file;
+ }
+ else
+ {
+ if (filename[0] == '.')
+ return 0;
+
+ if (n <= 5)
+ return 0;
+
+ if (grub_strcmp (filename + n - 5, ".conf") != 0)
+ return 0;
+
+ p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
+
+ f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
+ if (!f)
+ goto finish;
+ }
+
+ entry = grub_zalloc (sizeof (*entry));
+ if (!entry)
+ goto finish;
+
+ if (info->file)
+ {
+ char *slash;
+
+ if (n > 5 && !grub_strcmp (filename + n - 5, ".conf") == 0)
+ clip = 5;
+
+ slash = grub_strrchr (filename, '/');
+ if (!slash)
+ slash = grub_strrchr (filename, '\\');
+
+ while (*slash == '/' || *slash == '\\')
+ slash++;
+
+ m = slash ? slash - filename : 0;
+ }
+ else
+ {
+ m = 0;
+ clip = 5;
+ }
+ n -= m;
+
+ entry->filename = grub_strndup(filename + m, n - clip);
+ if (!entry->filename)
+ goto finish;
+
+ entry->filename[n - 5] = '\0';
+
+ for (;;)
+ {
+ char *buf;
+ char *separator;
+
+ buf = grub_file_getline (f);
+ if (!buf)
+ break;
+
+ while (buf && buf[0] && (buf[0] == ' ' || buf[0] == '\t'))
+ buf++;
+ if (buf[0] == '#')
+ continue;
+
+ separator = grub_strchr (buf, ' ');
+
+ if (!separator)
+ separator = grub_strchr (buf, '\t');
+
+ if (!separator || separator[1] == '\0')
+ {
+ grub_free (buf);
+ break;
+ }
+
+ separator[0] = '\0';
+
+ do {
+ separator++;
+ } while (*separator == ' ' || *separator == '\t');
+
+ rc = bls_add_keyval (entry, buf, separator);
+ grub_free (buf);
+ if (rc < 0)
+ break;
+ }
+
+ if (!rc)
+ bls_add_entry(entry);
+
+finish:
+ if (p)
+ grub_free (p);
+
+ if (f)
+ grub_file_close (f);
+
+ return 0;
+}
+
+static grub_envblk_t saved_env = NULL;
+
+static int UNUSED
+save_var (const char *name, const char *value, void *whitelist UNUSED)
+{
+ const char *val = grub_env_get (name);
+ grub_dprintf("blscfg", "saving \"%s\"\n", name);
+
+ if (val)
+ grub_envblk_set (saved_env, name, value);
+
+ return 0;
+}
+
+static int UNUSED
+unset_var (const char *name, const char *value UNUSED, void *whitelist)
+{
+ grub_dprintf("blscfg", "restoring \"%s\"\n", name);
+ if (! whitelist)
+ {
+ grub_env_unset (name);
+ return 0;
+ }
+
+ if (test_whitelist_membership (name,
+ (const grub_env_whitelist_t *) whitelist))
+ grub_env_unset (name);
+
+ return 0;
+}
+
+static char **bls_make_list (struct bls_entry *entry, const char *key, int *num)
+{
+ int last = -1;
+ char *val;
+
+ int nlist = 0;
+ char **list = NULL;
+
+ list = grub_malloc (sizeof (char *));
+ if (!list)
+ return NULL;
+ list[0] = NULL;
+
+ while (1)
+ {
+ char **new;
+
+ val = bls_get_val (entry, key, &last);
+ if (!val)
+ break;
+
+ new = grub_realloc (list, (nlist + 2) * sizeof (char *));
+ if (!new)
+ break;
+
+ list = new;
+ list[nlist++] = val;
+ list[nlist] = NULL;
+ }
+
+ if (!nlist)
+ {
+ grub_free (list);
+ return NULL;
+ }
+
+ if (num)
+ *num = nlist;
+
+ return list;
+}
+
+static char *field_append(bool is_var, char *buffer, const char *start, const char *end)
+{
+ char *tmp = grub_strndup(start, end - start + 1);
+ const char *field = tmp;
+ int term = is_var ? 2 : 1;
+
+ if (is_var) {
+ field = grub_env_get (tmp);
+ if (!field)
+ return buffer;
+ }
+
+ if (!buffer)
+ buffer = grub_zalloc (grub_strlen(field) + term);
+ else
+ buffer = grub_realloc (buffer, grub_strlen(buffer) + grub_strlen(field) + term);
+
+ if (!buffer)
+ return NULL;
+
+ tmp = buffer + grub_strlen(buffer);
+ tmp = grub_stpcpy (tmp, field);
+
+ if (is_var)
+ tmp = grub_stpcpy (tmp, " ");
+
+ return buffer;
+}
+
+static char *expand_val(const char *value)
+{
+ char *buffer = NULL;
+ const char *start = value;
+ const char *end = value;
+ bool is_var = false;
+
+ if (!value)
+ return NULL;
+
+ while (*value) {
+ if (*value == '$') {
+ if (start != end) {
+ buffer = field_append(is_var, buffer, start, end);
+ if (!buffer)
+ return NULL;
+ }
+
+ is_var = true;
+ start = value + 1;
+ } else if (is_var) {
+ if (!grub_isalnum(*value) && *value != '_') {
+ buffer = field_append(is_var, buffer, start, end);
+ is_var = false;
+ start = value;
+ if (*start == ' ')
+ start++;
+ }
+ }
+
+ end = value;
+ value++;
+ }
+
+ if (start != end) {
+ buffer = field_append(is_var, buffer, start, end);
+ if (!buffer)
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static char **early_initrd_list (const char *initrd)
+{
+ int nlist = 0;
+ char **list = NULL;
+ char *separator;
+
+ while ((separator = grub_strchr (initrd, ' ')))
+ {
+ list = grub_realloc (list, (nlist + 2) * sizeof (char *));
+ if (!list)
+ return NULL;
+
+ list[nlist++] = grub_strndup(initrd, separator - initrd);
+ list[nlist] = NULL;
+ initrd = separator + 1;
+ }
+
+ list = grub_realloc (list, (nlist + 2) * sizeof (char *));
+ if (!list)
+ return NULL;
+
+ list[nlist++] = grub_strndup(initrd, grub_strlen(initrd));
+ list[nlist] = NULL;
+
+ return list;
+}
+
+static void create_entry (struct bls_entry *entry)
+{
+ int argc = 0;
+ const char **argv = NULL;
+
+ char *title = NULL;
+ char *clinux = NULL;
+ char *options = NULL;
+ char **initrds = NULL;
+ char *initrd = NULL;
+ const char *early_initrd = NULL;
+ char **early_initrds = NULL;
+ char *initrd_prefix = NULL;
+ char *devicetree = NULL;
+ char *dt = NULL;
+ char *id = entry->filename;
+ char *dotconf = id;
+ char *hotkey = NULL;
+
+ char *users = NULL;
+ char **classes = NULL;
+
+ char **args = NULL;
+
+ char *src = NULL;
+ int i, index;
+ bool add_dt_prefix = false;
+
+ grub_dprintf("blscfg", "%s got here\n", __func__);
+ clinux = bls_get_val (entry, "linux", NULL);
+ if (!clinux)
+ {
+ grub_dprintf ("blscfg", "Skipping file %s with no 'linux' key.\n", entry->filename);
+ goto finish;
+ }
+
+ /*
+ * strip the ".conf" off the end before we make it our "id" field.
+ */
+ do
+ {
+ dotconf = grub_strstr(dotconf, ".conf");
+ } while (dotconf != NULL && dotconf[5] != '\0');
+ if (dotconf)
+ dotconf[0] = '\0';
+
+ title = bls_get_val (entry, "title", NULL);
+ options = expand_val (bls_get_val (entry, "options", NULL));
+
+ if (!options)
+ options = expand_val (grub_env_get("default_kernelopts"));
+
+ initrds = bls_make_list (entry, "initrd", NULL);
+
+ devicetree = expand_val (bls_get_val (entry, "devicetree", NULL));
+
+ if (!devicetree)
+ {
+ devicetree = expand_val (grub_env_get("devicetree"));
+ add_dt_prefix = true;
+ }
+
+ hotkey = bls_get_val (entry, "grub_hotkey", NULL);
+ users = expand_val (bls_get_val (entry, "grub_users", NULL));
+ classes = bls_make_list (entry, "grub_class", NULL);
+ args = bls_make_list (entry, "grub_arg", &argc);
+
+ argc += 1;
+ argv = grub_malloc ((argc + 1) * sizeof (char *));
+ argv[0] = title ? title : clinux;
+ for (i = 1; i < argc; i++)
+ argv[i] = args[i-1];
+ argv[argc] = NULL;
+
+ early_initrd = grub_env_get("early_initrd");
+
+ grub_dprintf ("blscfg", "adding menu entry for \"%s\" with id \"%s\"\n",
+ title, id);
+ if (early_initrd)
+ {
+ early_initrds = early_initrd_list(early_initrd);
+ if (!early_initrds)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto finish;
+ }
+
+ if (initrds != NULL && initrds[0] != NULL)
+ {
+ initrd_prefix = grub_strrchr (initrds[0], '/');
+ initrd_prefix = grub_strndup(initrds[0], initrd_prefix - initrds[0] + 1);
+ }
+ else
+ {
+ initrd_prefix = grub_strrchr (clinux, '/');
+ initrd_prefix = grub_strndup(clinux, initrd_prefix - clinux + 1);
+ }
+
+ if (!initrd_prefix)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto finish;
+ }
+ }
+
+ if (early_initrds || initrds)
+ {
+ int initrd_size = sizeof ("initrd");
+ char *tmp;
+
+ for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
+ initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \
+ + grub_strlen(initrd_prefix) \
+ + grub_strlen (early_initrds[i]) + 1;
+
+ for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
+ initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \
+ + grub_strlen (initrds[i]) + 1;
+ initrd_size += 1;
+
+ initrd = grub_malloc (initrd_size);
+ if (!initrd)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto finish;
+ }
+
+ tmp = grub_stpcpy(initrd, "initrd");
+ for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
+ {
+ grub_dprintf ("blscfg", "adding early initrd %s\n", early_initrds[i]);
+ tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
+ tmp = grub_stpcpy (tmp, initrd_prefix);
+ tmp = grub_stpcpy (tmp, early_initrds[i]);
+ grub_free(early_initrds[i]);
+ }
+
+ for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
+ {
+ grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]);
+ tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
+ tmp = grub_stpcpy (tmp, initrds[i]);
+ }
+ tmp = grub_stpcpy (tmp, "\n");
+ }
+
+ if (devicetree)
+ {
+ char *prefix = NULL;
+ int dt_size;
+
+ if (add_dt_prefix)
+ {
+ prefix = grub_strrchr (clinux, '/');
+ prefix = grub_strndup(clinux, prefix - clinux + 1);
+ if (!prefix)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto finish;
+ }
+ }
+
+ dt_size = sizeof("devicetree " GRUB_BOOT_DEVICE) + grub_strlen(devicetree) + 1;
+
+ if (add_dt_prefix)
+ {
+ dt_size += grub_strlen(prefix);
+ }
+
+ dt = grub_malloc (dt_size);
+ if (!dt)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto finish;
+ }
+ char *tmp = dt;
+ tmp = grub_stpcpy (dt, "devicetree");
+ tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
+ if (add_dt_prefix)
+ tmp = grub_stpcpy (tmp, prefix);
+ tmp = grub_stpcpy (tmp, devicetree);
+ tmp = grub_stpcpy (tmp, "\n");
+
+ grub_free(prefix);
+ }
+
+ grub_dprintf ("blscfg2", "devicetree %s for id:\"%s\"\n", dt, id);
+
+ const char *sdval = grub_env_get("save_default");
+ bool savedefault = ((NULL != sdval) && (grub_strcmp(sdval, "true") == 0));
+ src = grub_xasprintf ("%sload_video\n"
+ "set gfxpayload=keep\n"
+ "insmod gzio\n"
+ "linux %s%s%s%s\n"
+ "%s%s",
+ savedefault ? "savedefault\n" : "",
+ GRUB_BOOT_DEVICE, clinux, options ? " " : "", options ? options : "",
+ initrd ? initrd : "", dt ? dt : "");
+
+ grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry);
+ grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id);
+
+finish:
+ grub_free (dt);
+ grub_free (initrd);
+ grub_free (initrd_prefix);
+ grub_free (early_initrds);
+ grub_free (devicetree);
+ grub_free (initrds);
+ grub_free (options);
+ grub_free (classes);
+ grub_free (args);
+ grub_free (argv);
+ grub_free (src);
+}
+
+struct find_entry_info {
+ const char *dirname;
+ const char *devid;
+ grub_device_t dev;
+ grub_fs_t fs;
+};
+
+/*
+ * info: the filesystem object the file is on.
+ */
+static int find_entry (struct find_entry_info *info)
+{
+ struct read_entry_info read_entry_info;
+ grub_fs_t blsdir_fs = NULL;
+ grub_device_t blsdir_dev = NULL;
+ const char *blsdir = info->dirname;
+ int fallback = 0;
+ int r = 0;
+
+ if (!blsdir) {
+ blsdir = grub_env_get ("blsdir");
+ if (!blsdir)
+ blsdir = GRUB_BLS_CONFIG_PATH;
+ }
+
+ read_entry_info.file = NULL;
+ read_entry_info.dirname = blsdir;
+
+ grub_dprintf ("blscfg", "scanning blsdir: %s\n", blsdir);
+
+ blsdir_dev = info->dev;
+ blsdir_fs = info->fs;
+ read_entry_info.devid = info->devid;
+
+read_fallback:
+ r = blsdir_fs->fs_dir (blsdir_dev, read_entry_info.dirname, read_entry,
+ &read_entry_info);
+ if (r != 0) {
+ grub_dprintf ("blscfg", "read_entry returned error\n");
+ grub_err_t e;
+ do
+ {
+ e = grub_error_pop();
+ } while (e);
+ }
+
+ if (r && !info->dirname && !fallback) {
+ read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH;
+ grub_dprintf ("blscfg", "Entries weren't found in %s, fallback to %s\n",
+ blsdir, read_entry_info.dirname);
+ fallback = 1;
+ goto read_fallback;
+ }
+
+ return 0;
+}
+
+static grub_err_t
+bls_load_entries (const char *path)
+{
+ grub_size_t len;
+ grub_fs_t fs;
+ grub_device_t dev;
+ static grub_err_t r;
+ const char *devid = NULL;
+ char *blsdir = NULL;
+ struct find_entry_info info = {
+ .dev = NULL,
+ .fs = NULL,
+ .dirname = NULL,
+ };
+ struct read_entry_info rei = {
+ .devid = NULL,
+ .dirname = NULL,
+ };
+
+ if (path) {
+ len = grub_strlen (path);
+ if (grub_strcmp (path + len - 5, ".conf") == 0) {
+ rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
+ if (!rei.file)
+ return grub_errno;
+ /*
+ * read_entry() closes the file
+ */
+ return read_entry(path, NULL, &rei);
+ } else if (path[0] == '(') {
+ devid = path + 1;
+
+ blsdir = grub_strchr (path, ')');
+ if (!blsdir)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Filepath isn't correct"));
+
+ *blsdir = '\0';
+ blsdir = blsdir + 1;
+ }
+ }
+
+ if (!devid) {
+#ifdef GRUB_MACHINE_EMU
+ devid = "host";
+#else
+ devid = grub_env_get ("root");
+#endif
+ if (!devid)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ N_("variable `%s' isn't set"), "root");
+ }
+
+ grub_dprintf ("blscfg", "opening %s\n", devid);
+ dev = grub_device_open (devid);
+ if (!dev)
+ return grub_errno;
+
+ grub_dprintf ("blscfg", "probing fs\n");
+ fs = grub_fs_probe (dev);
+ if (!fs)
+ {
+ r = grub_errno;
+ goto finish;
+ }
+
+ info.dirname = blsdir;
+ info.devid = devid;
+ info.dev = dev;
+ info.fs = fs;
+ find_entry(&info);
+
+finish:
+ if (dev)
+ grub_device_close (dev);
+
+ return r;
+}
+
+static bool
+is_default_entry(const char *def_entry, struct bls_entry *entry, int idx)
+{
+ const char *title;
+ int def_idx;
+
+ if (!def_entry)
+ return false;
+
+ if (grub_strcmp(def_entry, entry->filename) == 0)
+ return true;
+
+ title = bls_get_val(entry, "title", NULL);
+
+ if (title && grub_strcmp(def_entry, title) == 0)
+ return true;
+
+ def_idx = (int)grub_strtol(def_entry, NULL, 0);
+ if (grub_errno == GRUB_ERR_BAD_NUMBER) {
+ grub_errno = GRUB_ERR_NONE;
+ return false;
+ }
+
+ if (def_idx == idx)
+ return true;
+
+ return false;
+}
+
+static grub_err_t
+bls_create_entries (bool show_default, bool show_non_default, char *entry_id)
+{
+ const char *def_entry = NULL;
+ struct bls_entry *entry = NULL;
+ int idx = 0;
+
+ def_entry = grub_env_get("default");
+
+ grub_dprintf ("blscfg", "%s Creating entries from bls\n", __func__);
+ FOR_BLS_ENTRIES(entry) {
+ if (entry->visible) {
+ idx++;
+ continue;
+ }
+
+ if ((show_default && is_default_entry(def_entry, entry, idx)) ||
+ (show_non_default && !is_default_entry(def_entry, entry, idx)) ||
+ (entry_id && grub_strcmp(entry_id, entry->filename) == 0)) {
+ create_entry(entry);
+ entry->visible = 1;
+ }
+ idx++;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_blscfg (grub_extcmd_context_t ctxt UNUSED,
+ int argc, char **args)
+{
+ grub_err_t r;
+ char *path = NULL;
+ char *entry_id = NULL;
+ bool show_default = true;
+ bool show_non_default = true;
+
+ if (argc == 1) {
+ if (grub_strcmp (args[0], "default") == 0) {
+ show_non_default = false;
+ } else if (grub_strcmp (args[0], "non-default") == 0) {
+ show_default = false;
+ } else if (args[0][0] == '(') {
+ path = args[0];
+ } else {
+ entry_id = args[0];
+ show_default = false;
+ show_non_default = false;
+ }
+ }
+
+ r = bls_load_entries(path);
+ if (r)
+ return r;
+
+ return bls_create_entries(show_default, show_non_default, entry_id);
+}
+
+static grub_extcmd_t cmd;
+static grub_extcmd_t oldcmd;
+
+GRUB_MOD_INIT(blscfg)
+{
+ grub_dprintf("blscfg", "%s got here\n", __func__);
+ cmd = grub_register_extcmd ("blscfg",
+ grub_cmd_blscfg,
+ 0,
+ NULL,
+ N_("Import Boot Loader Specification snippets."),
+ NULL);
+ oldcmd = grub_register_extcmd ("bls_import",
+ grub_cmd_blscfg,
+ 0,
+ NULL,
+ N_("Import Boot Loader Specification snippets."),
+ NULL);
+}
+
+GRUB_MOD_FINI(blscfg)
+{
+ grub_unregister_extcmd (cmd);
+ grub_unregister_extcmd (oldcmd);
+}
diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
index cc5971f4dbd..782761c31aa 100644
--- a/grub-core/commands/legacycfg.c
+++ b/grub-core/commands/legacycfg.c
@@ -143,7 +143,7 @@ legacy_file (const char *filename)
args[0] = oldname;
grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
NULL, NULL,
- entrysrc, 0);
+ entrysrc, 0, NULL, NULL);
grub_free (args);
entrysrc[0] = 0;
grub_free (oldname);
@@ -205,7 +205,8 @@ legacy_file (const char *filename)
}
args[0] = entryname;
grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
- NULL, NULL, entrysrc, 0);
+ NULL, NULL, entrysrc, 0, NULL,
+ NULL);
grub_free (args);
}
diff --git a/grub-core/commands/loadenv.c b/grub-core/commands/loadenv.c
index 3fd664aac33..163b9a09042 100644
--- a/grub-core/commands/loadenv.c
+++ b/grub-core/commands/loadenv.c
@@ -28,6 +28,8 @@
#include <grub/extcmd.h>
#include <grub/i18n.h>
+#include "loadenv.h"
+
GRUB_MOD_LICENSE ("GPLv3+");
static const struct grub_arg_option options[] =
@@ -79,81 +81,6 @@ open_envblk_file (char *filename,
return file;
}
-static grub_envblk_t
-read_envblk_file (grub_file_t file)
-{
- grub_off_t offset = 0;
- char *buf;
- grub_size_t size = grub_file_size (file);
- grub_envblk_t envblk;
-
- buf = grub_malloc (size);
- if (! buf)
- return 0;
-
- while (size > 0)
- {
- grub_ssize_t ret;
-
- ret = grub_file_read (file, buf + offset, size);
- if (ret <= 0)
- {
- grub_free (buf);
- return 0;
- }
-
- size -= ret;
- offset += ret;
- }
-
- envblk = grub_envblk_open (buf, offset);
- if (! envblk)
- {
- grub_free (buf);
- grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
- return 0;
- }
-
- return envblk;
-}
-
-struct grub_env_whitelist
-{
- grub_size_t len;
- char **list;
-};
-typedef struct grub_env_whitelist grub_env_whitelist_t;
-
-static int
-test_whitelist_membership (const char* name,
- const grub_env_whitelist_t* whitelist)
-{
- grub_size_t i;
-
- for (i = 0; i < whitelist->len; i++)
- if (grub_strcmp (name, whitelist->list[i]) == 0)
- return 1; /* found it */
-
- return 0; /* not found */
-}
-
-/* Helper for grub_cmd_load_env. */
-static int
-set_var (const char *name, const char *value, void *whitelist)
-{
- if (! whitelist)
- {
- grub_env_set (name, value);
- return 0;
- }
-
- if (test_whitelist_membership (name,
- (const grub_env_whitelist_t *) whitelist))
- grub_env_set (name, value);
-
- return 0;
-}
-
static grub_err_t
grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
{
diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
index 720e6d8ea3b..b194123eb67 100644
--- a/grub-core/commands/menuentry.c
+++ b/grub-core/commands/menuentry.c
@@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
char **classes, const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
- int submenu)
+ int submenu, int *index, struct bls_entry *bls)
{
int menu_hotkey = 0;
char **menu_args = NULL;
@@ -149,9 +149,12 @@ grub_normal_add_menu_entry (int argc, const char **args,
if (! menu_title)
goto fail;
+ grub_dprintf ("menu", "id:\"%s\"\n", id);
+ grub_dprintf ("menu", "title:\"%s\"\n", menu_title);
menu_id = grub_strdup (id ? : menu_title);
if (! menu_id)
goto fail;
+ grub_dprintf ("menu", "menu_id:\"%s\"\n", menu_id);
/* Save argc, args to pass as parameters to block arg later. */
menu_args = grub_calloc (argc + 1, sizeof (char *));
@@ -170,8 +173,12 @@ grub_normal_add_menu_entry (int argc, const char **args,
}
/* Add the menu entry at the end of the list. */
+ int ind=0;
while (*last)
- last = &(*last)->next;
+ {
+ ind++;
+ last = &(*last)->next;
+ }
*last = grub_zalloc (sizeof (**last));
if (! *last)
@@ -188,8 +195,11 @@ grub_normal_add_menu_entry (int argc, const char **args,
(*last)->args = menu_args;
(*last)->sourcecode = menu_sourcecode;
(*last)->submenu = submenu;
+ (*last)->bls = bls;
menu->size++;
+ if (index)
+ *index = ind;
return GRUB_ERR_NONE;
fail:
@@ -286,7 +296,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
users,
ctxt->state[2].arg, 0,
ctxt->state[3].arg,
- ctxt->extcmd->cmd->name[0] == 's');
+ ctxt->extcmd->cmd->name[0] == 's',
+ NULL, NULL);
src = args[argc - 1];
args[argc - 1] = NULL;
@@ -303,7 +314,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
ctxt->state[0].args, ctxt->state[4].arg,
users,
ctxt->state[2].arg, prefix, src + 1,
- ctxt->extcmd->cmd->name[0] == 's');
+ ctxt->extcmd->cmd->name[0] == 's', NULL,
+ NULL);
src[len - 1] = ch;
args[argc - 1] = src;
diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
index 62571e6dfcc..7ca2e5400b1 100644
--- a/grub-core/normal/main.c
+++ b/grub-core/normal/main.c
@@ -21,6 +21,7 @@
#include <grub/net.h>
#include <grub/normal.h>
#include <grub/dl.h>
+#include <grub/menu.h>
#include <grub/misc.h>
#include <grub/file.h>
#include <grub/mm.h>
@@ -70,6 +71,11 @@ grub_normal_free_menu (grub_menu_t menu)
grub_free (entry->args);
}
+ if (entry->bls)
+ {
+ entry->bls->visible = 0;
+ }
+
grub_free ((void *) entry->id);
grub_free ((void *) entry->users);
grub_free ((void *) entry->title);
diff --git a/grub-core/commands/loadenv.h b/grub-core/commands/loadenv.h
new file mode 100644
index 00000000000..952f46121bd
--- /dev/null
+++ b/grub-core/commands/loadenv.h
@@ -0,0 +1,93 @@
+/* loadenv.c - command to load/save environment variable. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+static grub_envblk_t UNUSED
+read_envblk_file (grub_file_t file)
+{
+ grub_off_t offset = 0;
+ char *buf;
+ grub_size_t size = grub_file_size (file);
+ grub_envblk_t envblk;
+
+ buf = grub_malloc (size);
+ if (! buf)
+ return 0;
+
+ while (size > 0)
+ {
+ grub_ssize_t ret;
+
+ ret = grub_file_read (file, buf + offset, size);
+ if (ret <= 0)
+ {
+ grub_free (buf);
+ return 0;
+ }
+
+ size -= ret;
+ offset += ret;
+ }
+
+ envblk = grub_envblk_open (buf, offset);
+ if (! envblk)
+ {
+ grub_free (buf);
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
+ return 0;
+ }
+
+ return envblk;
+}
+
+struct grub_env_whitelist
+{
+ grub_size_t len;
+ char **list;
+};
+typedef struct grub_env_whitelist grub_env_whitelist_t;
+
+static int UNUSED
+test_whitelist_membership (const char* name,
+ const grub_env_whitelist_t* whitelist)
+{
+ grub_size_t i;
+
+ for (i = 0; i < whitelist->len; i++)
+ if (grub_strcmp (name, whitelist->list[i]) == 0)
+ return 1; /* found it */
+
+ return 0; /* not found */
+}
+
+/* Helper for grub_cmd_load_env. */
+static int UNUSED
+set_var (const char *name, const char *value, void *whitelist)
+{
+ if (! whitelist)
+ {
+ grub_env_set (name, value);
+ return 0;
+ }
+
+ if (test_whitelist_membership (name,
+ (const grub_env_whitelist_t *) whitelist))
+ grub_env_set (name, value);
+
+ return 0;
+}
diff --git a/include/grub/compiler.h b/include/grub/compiler.h
index 8f3be3ae706..ebafec68957 100644
--- a/include/grub/compiler.h
+++ b/include/grub/compiler.h
@@ -56,4 +56,6 @@
# define CLANG_PREREQ(maj,min) 0
#endif
+#define UNUSED __attribute__((__unused__))
+
#endif /* ! GRUB_COMPILER_HEADER */
diff --git a/include/grub/menu.h b/include/grub/menu.h
index ee2b5e91045..0acdc2aa6bf 100644
--- a/include/grub/menu.h
+++ b/include/grub/menu.h
@@ -20,6 +20,16 @@
#ifndef GRUB_MENU_HEADER
#define GRUB_MENU_HEADER 1
+struct bls_entry
+{
+ struct bls_entry *next;
+ struct bls_entry *prev;
+ struct keyval **keyvals;
+ int nkeyvals;
+ char *filename;
+ int visible;
+};
+
struct grub_menu_entry_class
{
char *name;
@@ -60,6 +70,9 @@ struct grub_menu_entry
/* The next element. */
struct grub_menu_entry *next;
+
+ /* BLS used to populate the entry */
+ struct bls_entry *bls;
};
typedef struct grub_menu_entry *grub_menu_entry_t;
diff --git a/include/grub/normal.h b/include/grub/normal.h
index 218cbabccaf..8839ad85a19 100644
--- a/include/grub/normal.h
+++ b/include/grub/normal.h
@@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes,
const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
- int submenu);
+ int submenu, int *index, struct bls_entry *bls);
grub_err_t
grub_normal_set_password (const char *user, const char *password);