1348 lines
37 KiB
Diff
1348 lines
37 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Peter Jones <pjones@redhat.com>
|
|
Date: Tue, 12 Aug 2025 03:45:33 +0000
|
|
Subject: [PATCH] blsuki: Add blscfg command 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.
|
|
|
|
The BLS Specification: https://uapi-group.org/specifications/specs/boot_loader_specification/
|
|
|
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
|
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
|
|
Signed-off-by: Will Thompson <wjt@endlessm.com>
|
|
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
|
|
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
|
|
---
|
|
bootstrap.conf | 1 +
|
|
docs/grub.texi | 74 +++
|
|
grub-core/Makefile.core.def | 12 +
|
|
grub-core/commands/blscfg.c | 2 +-
|
|
grub-core/commands/blsuki.c | 1028 ++++++++++++++++++++++++++++++++++++++++
|
|
grub-core/commands/legacycfg.c | 4 +-
|
|
grub-core/commands/menuentry.c | 7 +-
|
|
grub-core/normal/main.c | 4 +
|
|
include/grub/menu.h | 14 +
|
|
include/grub/normal.h | 2 +-
|
|
10 files changed, 1141 insertions(+), 7 deletions(-)
|
|
create mode 100644 grub-core/commands/blsuki.c
|
|
|
|
diff --git a/bootstrap.conf b/bootstrap.conf
|
|
index 60de0597c5..799cdb1a35 100644
|
|
--- a/bootstrap.conf
|
|
+++ b/bootstrap.conf
|
|
@@ -24,6 +24,7 @@ gnulib_modules="
|
|
argp
|
|
base64
|
|
error
|
|
+ filevercmp
|
|
fnmatch
|
|
getdelim
|
|
getline
|
|
diff --git a/docs/grub.texi b/docs/grub.texi
|
|
index 9f5eb68749..e795719abe 100644
|
|
--- a/docs/grub.texi
|
|
+++ b/docs/grub.texi
|
|
@@ -3276,6 +3276,7 @@ These variables have special meaning to GRUB.
|
|
@menu
|
|
* appendedsig_key_mgmt::
|
|
* biosnum::
|
|
+* blsuki_save_default::
|
|
* check_appended_signatures::
|
|
* check_signatures::
|
|
* chosen::
|
|
@@ -3353,6 +3354,12 @@ this.
|
|
For an alternative approach which also changes BIOS drive mappings for the
|
|
chain-loaded system, @pxref{drivemap}.
|
|
|
|
+@node blsuki_save_default
|
|
+@subsection blsuki_save_default
|
|
+
|
|
+If this variable is set, menu entries generated from BLS config files
|
|
+(@pxref{blscfg}) will be set as the default boot entry when selected.
|
|
+
|
|
@node check_appended_signatures
|
|
@subsection check_appended_signatures
|
|
|
|
@@ -4380,6 +4387,7 @@ you forget a command, you can run the command @command{help}
|
|
* background_image:: Load background image for active terminal
|
|
* badram:: Filter out bad regions of RAM
|
|
* blocklist:: Print a block list
|
|
+* blscfg:: Load Boot Loader Specification menu entries
|
|
* boot:: Start up your operating system
|
|
* cat:: Show the contents of a file
|
|
* clear:: Clear the screen
|
|
@@ -4700,6 +4708,72 @@ Print a block list (@pxref{Block list syntax}) for @var{file}.
|
|
@end deffn
|
|
|
|
|
|
+@node blscfg
|
|
+@subsection blscfg
|
|
+
|
|
+@deffn Command blscfg [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file]
|
|
+Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot entries
|
|
+generated from @command{blscfg} won't interfere with entries from @file{grub.cfg} appearing in
|
|
+the GRUB menu. Also, entries generated from @command{blscfg} exists only in memory and
|
|
+don't update @file{grub.cfg}.
|
|
+
|
|
+By default, the BLS entries are stored in the @file{/loader/entries} directory in the
|
|
+boot partition. If BLS entries are stored elsewhere, the @option{--path} option can be
|
|
+used to check a different directory instead of the default location. If no BLS
|
|
+entries are found while using the @option{--path} option, the @option{--enable-fallback} option
|
|
+can be used to check for entries in the default location.
|
|
+
|
|
+The @option{--show-default} option allows the default boot entry to be added to the
|
|
+GRUB menu from the BLS entries.
|
|
+
|
|
+The @option{--show-non-default} option allows non-default boot entries to be added to
|
|
+the GRUB menu from the BLS entries.
|
|
+
|
|
+The @option{--entry} option allows specific boot entries to be added to the GRUB menu
|
|
+from the BLS entries.
|
|
+
|
|
+The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
|
|
+are used to filter which BLS entries are added to the GRUB menu. If none are
|
|
+used, all entries in the default location or the location specified by @option{--path}
|
|
+will be added to the GRUB menu.
|
|
+
|
|
+A BLS config file example:
|
|
+@example
|
|
+# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
|
|
+title Fedora 19 (Rawhide)
|
|
+sort-key fedora
|
|
+machine-id 6a9857a393724b7a981ebb5b8495b9ea
|
|
+version 3.8.0-2.fc19.x86_64
|
|
+options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet
|
|
+architecture x64
|
|
+linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
|
|
+initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd
|
|
+@end example
|
|
+
|
|
+For more information on BLS entry keys as well as other information on BLS,
|
|
+see: @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/, The Boot Loader Specification}. For the GRUB, there are a few additional
|
|
+BLS entry keys based on the @command{menuentry} command (@pxref{menuentry}).
|
|
+
|
|
+The @code{grub_class} key may be used any number of times to group menu entries into
|
|
+classes. Menu themes may display different classes using different styles.
|
|
+
|
|
+The @code{grub_users} key grants specific users access to specific menu
|
|
+entries. @xref{Security}.
|
|
+
|
|
+The @code{grub_hotkey} key associates a hotkey with a menu entry.
|
|
+@var{key} may be a single letter, or one of the aliases @samp{backspace},
|
|
+@samp{tab}, or @samp{delete}.
|
|
+
|
|
+The @code{grub_args} key can be used for any other argument to be passed as positonal
|
|
+parameters when the list of commands generated from the BLS config file are
|
|
+executed.
|
|
+
|
|
+Variable expansion using the @samp{$} character (@xref{Shell-like scripting}) may be
|
|
+used with BLS config files for the GRUB but might not be compatible with other
|
|
+bootloaders.
|
|
+@end deffn
|
|
+
|
|
+
|
|
@node boot
|
|
@subsection boot
|
|
|
|
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
|
|
index 1f464de0ff..78fafdd8fa 100644
|
|
--- a/grub-core/Makefile.core.def
|
|
+++ b/grub-core/Makefile.core.def
|
|
@@ -884,6 +884,18 @@ module = {
|
|
enable = emu;
|
|
};
|
|
|
|
+module = {
|
|
+ name = blsuki;
|
|
+ common = commands/blsuki.c;
|
|
+ common = lib/gnulib/filevercmp.c;
|
|
+ enable = powerpc_ieee1275;
|
|
+ enable = efi;
|
|
+ enable = i386_pc;
|
|
+ enable = emu;
|
|
+ cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
|
|
+ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
|
|
+};
|
|
+
|
|
module = {
|
|
name = boot;
|
|
common = commands/boot.c;
|
|
diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c
|
|
index 5d931b0c9b..38913d6964 100644
|
|
--- a/grub-core/commands/blscfg.c
|
|
+++ b/grub-core/commands/blscfg.c
|
|
@@ -955,7 +955,7 @@ static void create_entry (struct bls_entry *entry)
|
|
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_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry, NULL);
|
|
grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id);
|
|
|
|
finish:
|
|
diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
|
|
new file mode 100644
|
|
index 0000000000..9440adb100
|
|
--- /dev/null
|
|
+++ b/grub-core/commands/blsuki.c
|
|
@@ -0,0 +1,1028 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2025 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/>.
|
|
+ */
|
|
+
|
|
+#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/safemath.h>
|
|
+#include <grub/lib/envblk.h>
|
|
+#include <filevercmp.h>
|
|
+
|
|
+GRUB_MOD_LICENSE ("GPLv3+");
|
|
+
|
|
+#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
|
|
+
|
|
+#define BLS_EXT_LEN (sizeof (".conf") - 1)
|
|
+
|
|
+/*
|
|
+ * It is highly unlikely to ever receive a large amount of keyval pairs. A
|
|
+ * limit of 10000 is more than enough.
|
|
+ */
|
|
+#define BLSUKI_KEYVALS_MAX 10000
|
|
+
|
|
+static const struct grub_arg_option bls_opt[] =
|
|
+ {
|
|
+ {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"), ARG_TYPE_PATHNAME},
|
|
+ {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find BLS entries.", 0, ARG_TYPE_NONE},
|
|
+ {"show-default", 'd', 0, "Allow the default BLS entry to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
|
|
+ {"show-non-default", 'n', 0, "Allow the non-default BLS entries to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
|
|
+ {"entry", 'e', 0, "Allow specific BLS entries to be added to the GRUB menu.", N_("FILE"), ARG_TYPE_FILE},
|
|
+ {0, 0, 0, 0, 0, 0}
|
|
+ };
|
|
+
|
|
+struct keyval
|
|
+{
|
|
+ const char *key;
|
|
+ char *val;
|
|
+};
|
|
+
|
|
+struct read_entry_info
|
|
+{
|
|
+ const char *devid;
|
|
+ const char *dirname;
|
|
+ grub_file_t file;
|
|
+};
|
|
+
|
|
+struct find_entry_info
|
|
+{
|
|
+ const char *dirname;
|
|
+ const char *devid;
|
|
+ grub_device_t dev;
|
|
+ grub_fs_t fs;
|
|
+};
|
|
+
|
|
+static grub_blsuki_entry_t *entries;
|
|
+
|
|
+#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
|
|
+
|
|
+/*
|
|
+ * This function will add a new keyval pair to a list of keyvals stored in the
|
|
+ * entry parameter.
|
|
+ */
|
|
+static grub_err_t
|
|
+blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val)
|
|
+{
|
|
+ char *k, *v;
|
|
+ struct keyval **kvs, *kv;
|
|
+ grub_size_t size;
|
|
+ int new_n = entry->nkeyvals + 1;
|
|
+
|
|
+ if (new_n > BLSUKI_KEYVALS_MAX)
|
|
+ return grub_error (GRUB_ERR_BAD_NUMBER, "too many keyval pairs");
|
|
+
|
|
+ if (entry->keyvals_size == 0)
|
|
+ {
|
|
+ size = sizeof (struct keyval *);
|
|
+ kvs = grub_malloc (size);
|
|
+ if (kvs == NULL)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for BLS key values");
|
|
+
|
|
+ entry->keyvals = kvs;
|
|
+ entry->keyvals_size = size;
|
|
+ }
|
|
+ else if (entry->keyvals_size < new_n * sizeof (struct keyval *))
|
|
+ {
|
|
+ size = entry->keyvals_size * 2;
|
|
+ kvs = grub_realloc (entry->keyvals, size);
|
|
+ if (kvs == NULL)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate space for BLS key values");
|
|
+
|
|
+ entry->keyvals = kvs;
|
|
+ entry->keyvals_size = size;
|
|
+ }
|
|
+
|
|
+ kv = grub_malloc (sizeof (struct keyval));
|
|
+ if (kv == NULL)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
|
|
+
|
|
+ k = grub_strdup (key);
|
|
+ if (k == NULL)
|
|
+ {
|
|
+ grub_free (kv);
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
|
|
+ }
|
|
+
|
|
+ v = grub_strdup (val);
|
|
+ if (v == NULL)
|
|
+ {
|
|
+ grub_free (k);
|
|
+ grub_free (kv);
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
|
|
+ }
|
|
+
|
|
+ kv->key = k;
|
|
+ kv->val = v;
|
|
+ entry->keyvals[entry->nkeyvals] = kv;
|
|
+ entry->nkeyvals = new_n;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Find the value of the key named by keyname. If there are allowed to be
|
|
+ * more than one, pass a pointer set to -1 to the last parameter 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 *
|
|
+blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
|
|
+{
|
|
+ int idx, start = (last != NULL) ? (*last + 1) : 0;
|
|
+ struct keyval *kv = NULL;
|
|
+ char *ret = NULL;
|
|
+
|
|
+ for (idx = start; idx < entry->nkeyvals; idx++)
|
|
+ {
|
|
+ kv = entry->keyvals[idx];
|
|
+
|
|
+ if (grub_strcmp (keyname, kv->key) == 0)
|
|
+ {
|
|
+ ret = kv->val;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (last != NULL)
|
|
+ {
|
|
+ if (idx == entry->nkeyvals)
|
|
+ *last = -1;
|
|
+ else
|
|
+ *last = idx;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add a new grub_blsuki_entry_t struct to the entries list and sort it's
|
|
+ * position on the list.
|
|
+ */
|
|
+static grub_err_t
|
|
+blsuki_add_entry (grub_blsuki_entry_t *entry)
|
|
+{
|
|
+ grub_blsuki_entry_t *e, *last = NULL;
|
|
+ int rc;
|
|
+
|
|
+ if (entries == NULL)
|
|
+ {
|
|
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
|
|
+ entries = entry;
|
|
+ return GRUB_ERR_NONE;
|
|
+ }
|
|
+
|
|
+ FOR_BLSUKI_ENTRIES (e)
|
|
+ {
|
|
+ rc = filevercmp (entry->filename, e->filename);
|
|
+ if (rc == 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("duplicate file: `%s'"), entry->filename);
|
|
+
|
|
+ if (rc > 0)
|
|
+ {
|
|
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
|
|
+ grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry));
|
|
+ if (entry->next == entries)
|
|
+ {
|
|
+ entries = entry;
|
|
+ entry->prev = NULL;
|
|
+ }
|
|
+ else if (last != NULL)
|
|
+ last->next = entry;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+ }
|
|
+ last = e;
|
|
+ }
|
|
+
|
|
+ if (last != NULL)
|
|
+ {
|
|
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
|
|
+ last->next = entry;
|
|
+ entry->prev = &last;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function parses each line of a BLS config file to obtain the key value
|
|
+ * pairs that will be used to setup the GRUB menu entries. The key value pair
|
|
+ * will be stored in a list in the entry parameter.
|
|
+ */
|
|
+static grub_err_t
|
|
+bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
|
|
+{
|
|
+ grub_err_t err = GRUB_ERR_NONE;
|
|
+
|
|
+ for (;;)
|
|
+ {
|
|
+ char *line, *key, *val;
|
|
+
|
|
+ line = grub_file_getline (f);
|
|
+ if (line == NULL)
|
|
+ break;
|
|
+
|
|
+ key = grub_strtok_r (line, " \t", &val);
|
|
+ if (key == NULL)
|
|
+ {
|
|
+ grub_free (line);
|
|
+ break;
|
|
+ }
|
|
+ if (*key == '#')
|
|
+ {
|
|
+ grub_free (line);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ while (*val == ' ' || *val == '\t')
|
|
+ val++;
|
|
+
|
|
+ if (*val == '\0')
|
|
+ {
|
|
+ grub_free (line);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ err = blsuki_add_keyval (entry, key, val);
|
|
+ grub_free (line);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * If a file hasn't already been opened, this function opens a BLS config file
|
|
+ * and initializes entry data before parsing keyvals and adding the entry to
|
|
+ * the list of BLS entries.
|
|
+ */
|
|
+static int
|
|
+blsuki_read_entry (const char *filename,
|
|
+ const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)),
|
|
+ void *data)
|
|
+{
|
|
+ grub_size_t path_len = 0, filename_len;
|
|
+ grub_err_t err;
|
|
+ char *p = NULL;
|
|
+ grub_file_t f = NULL;
|
|
+ grub_blsuki_entry_t *entry;
|
|
+ struct read_entry_info *info = (struct read_entry_info *) data;
|
|
+
|
|
+ grub_dprintf ("blsuki", "filename: \"%s\"\n", filename);
|
|
+
|
|
+ filename_len = grub_strlen (filename);
|
|
+
|
|
+ if (info->file != NULL)
|
|
+ f = info->file;
|
|
+ else
|
|
+ {
|
|
+ if (filename_len < BLS_EXT_LEN ||
|
|
+ grub_strcmp (filename + filename_len - BLS_EXT_LEN, ".conf") != 0)
|
|
+ return 0;
|
|
+
|
|
+ p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
|
|
+
|
|
+ f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
|
|
+ grub_free (p);
|
|
+ if (f == NULL)
|
|
+ goto finish;
|
|
+ }
|
|
+
|
|
+ entry = grub_zalloc (sizeof (*entry));
|
|
+ if (entry == NULL)
|
|
+ goto finish;
|
|
+
|
|
+ /*
|
|
+ * If a file is opened before this function, the filename may have a path.
|
|
+ * Since the filename is used for the ID of the GRUB menu entry, we can
|
|
+ * remove the path.
|
|
+ */
|
|
+ if (info->file != NULL)
|
|
+ {
|
|
+ char *slash;
|
|
+
|
|
+ slash = grub_strrchr (filename, '/');
|
|
+ if (slash != NULL)
|
|
+ path_len = slash - filename + 1;
|
|
+ }
|
|
+ filename_len -= path_len;
|
|
+
|
|
+ entry->filename = grub_strndup (filename + path_len, filename_len);
|
|
+ if (entry->filename == NULL)
|
|
+ {
|
|
+ grub_free (entry);
|
|
+ goto finish;
|
|
+ }
|
|
+
|
|
+ err = bls_parse_keyvals (f, entry);
|
|
+
|
|
+ if (err == GRUB_ERR_NONE)
|
|
+ blsuki_add_entry (entry);
|
|
+ else
|
|
+ grub_free (entry);
|
|
+
|
|
+ finish:
|
|
+ if (f != NULL)
|
|
+ grub_file_close (f);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function returns a list of values that had the same key in the BLS
|
|
+ * config file. The number of entries in this list is returned by the len
|
|
+ * parameter.
|
|
+ */
|
|
+static char **
|
|
+blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int *len)
|
|
+{
|
|
+ int last = -1;
|
|
+ char *val;
|
|
+ int nlist = 0;
|
|
+ char **list;
|
|
+
|
|
+ list = grub_zalloc (sizeof (char *));
|
|
+ if (list == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ while (1)
|
|
+ {
|
|
+ char **new;
|
|
+
|
|
+ /*
|
|
+ * Since the same key might appear more than once, the 'last' variable
|
|
+ * starts at -1 and increments to indicate the last index in the list
|
|
+ * we obtained from blsuki_get_val().
|
|
+ */
|
|
+ val = blsuki_get_val (entry, key, &last);
|
|
+ if (val == NULL)
|
|
+ break;
|
|
+
|
|
+ new = grub_realloc (list, (nlist + 2) * sizeof (char *));
|
|
+ if (new == NULL)
|
|
+ break;
|
|
+
|
|
+ list = new;
|
|
+ list[nlist++] = val;
|
|
+ list[nlist] = NULL;
|
|
+ }
|
|
+
|
|
+ if (nlist == 0)
|
|
+ {
|
|
+ grub_free (list);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (len != NULL)
|
|
+ *len = nlist;
|
|
+
|
|
+ return list;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function appends a field to the end of a buffer. If the field given is
|
|
+ * an enviornmental variable, it gets the value stored for that variable and
|
|
+ * appends that to the buffer instead.
|
|
+ */
|
|
+static char *
|
|
+blsuki_field_append (bool is_env_var, char *buffer, const char *start, const char *end)
|
|
+{
|
|
+ char *tmp;
|
|
+ const char *field;
|
|
+ grub_size_t size = 0;
|
|
+
|
|
+ tmp = grub_strndup (start, end - start + 1);
|
|
+ if (tmp == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ field = tmp;
|
|
+
|
|
+ if (is_env_var == true)
|
|
+ {
|
|
+ field = grub_env_get (tmp);
|
|
+ if (field == NULL)
|
|
+ return buffer;
|
|
+ }
|
|
+
|
|
+ if (grub_add (grub_strlen (field), 1, &size))
|
|
+ return NULL;
|
|
+
|
|
+ if (buffer == NULL)
|
|
+ buffer = grub_zalloc (size);
|
|
+ else
|
|
+ {
|
|
+ if (grub_add (size, grub_strlen (buffer), &size))
|
|
+ return NULL;
|
|
+
|
|
+ buffer = grub_realloc (buffer, size);
|
|
+ }
|
|
+
|
|
+ if (buffer == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ tmp = buffer + grub_strlen (buffer);
|
|
+ tmp = grub_stpcpy (tmp, field);
|
|
+
|
|
+ if (is_env_var == true)
|
|
+ tmp = grub_stpcpy (tmp, " ");
|
|
+
|
|
+ return buffer;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function takes a value string, checks for environmental variables, and
|
|
+ * returns the value string with all environmental variables replaced with the
|
|
+ * value stored in the variable.
|
|
+ */
|
|
+static char *
|
|
+blsuki_expand_val (const char *value)
|
|
+{
|
|
+ char *buffer = NULL;
|
|
+ const char *start = value;
|
|
+ const char *end = value;
|
|
+ bool is_env_var = false;
|
|
+
|
|
+ if (value == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ while (*value != '\0')
|
|
+ {
|
|
+ if (*value == '$')
|
|
+ {
|
|
+ if (start != end)
|
|
+ {
|
|
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
|
|
+ if (buffer == NULL)
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ is_env_var = true;
|
|
+ start = value + 1;
|
|
+ }
|
|
+ else if (is_env_var == true)
|
|
+ {
|
|
+ if (grub_isalnum (*value) == 0 && *value != '_')
|
|
+ {
|
|
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
|
|
+ is_env_var = false;
|
|
+ start = value;
|
|
+ if (*start == ' ')
|
|
+ start++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ end = value;
|
|
+ value++;
|
|
+ }
|
|
+
|
|
+ if (start != end)
|
|
+ {
|
|
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
|
|
+ if (buffer == NULL)
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return buffer;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function returns a string with the command to load a linux kernel with
|
|
+ * kernel command-line options based on what was specified in the BLS config
|
|
+ * file.
|
|
+ */
|
|
+static char *
|
|
+bls_get_linux (grub_blsuki_entry_t *entry)
|
|
+{
|
|
+ char *linux_path;
|
|
+ char *linux_cmd = NULL;
|
|
+ char *options = NULL;
|
|
+ char *tmp;
|
|
+ grub_size_t size;
|
|
+
|
|
+ linux_path = blsuki_get_val (entry, "linux", NULL);
|
|
+ options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL));
|
|
+
|
|
+ if (grub_add (sizeof ("linux "), grub_strlen (linux_path), &size))
|
|
+ {
|
|
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
|
|
+ goto finish;
|
|
+ }
|
|
+
|
|
+ if (options != NULL)
|
|
+ {
|
|
+ if (grub_add (size, grub_strlen (options), &size) ||
|
|
+ grub_add (size, 1, &size))
|
|
+ {
|
|
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
|
|
+ goto finish;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ linux_cmd = grub_malloc (size);
|
|
+ if (linux_cmd == NULL)
|
|
+ goto finish;
|
|
+
|
|
+ tmp = linux_cmd;
|
|
+ tmp = grub_stpcpy (tmp, "linux ");
|
|
+ tmp = grub_stpcpy (tmp, linux_path);
|
|
+ if (options != NULL)
|
|
+ {
|
|
+ tmp = grub_stpcpy (tmp, " ");
|
|
+ tmp = grub_stpcpy (tmp, options);
|
|
+ }
|
|
+
|
|
+ tmp = grub_stpcpy (tmp, "\n");
|
|
+
|
|
+ finish:
|
|
+ grub_free (options);
|
|
+ return linux_cmd;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function returns a string with the command to load all initrds for a
|
|
+ * linux kernel image based on the list provided by the BLS config file.
|
|
+ */
|
|
+static char *
|
|
+bls_get_initrd (grub_blsuki_entry_t *entry)
|
|
+{
|
|
+ char **initrd_list;
|
|
+ char *initrd_cmd = NULL;
|
|
+ char *tmp;
|
|
+ grub_size_t size;
|
|
+ int i;
|
|
+
|
|
+ initrd_list = blsuki_make_list (entry, "initrd", NULL);
|
|
+ if (initrd_list != NULL)
|
|
+ {
|
|
+ size = sizeof ("initrd");
|
|
+
|
|
+ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
|
|
+ {
|
|
+ if (grub_add (size, 1, &size) ||
|
|
+ grub_add (size, grub_strlen (initrd_list[i]), &size))
|
|
+ {
|
|
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
|
|
+ goto finish;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (grub_add (size, 1, &size))
|
|
+ {
|
|
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
|
|
+ goto finish;
|
|
+ }
|
|
+
|
|
+ initrd_cmd = grub_malloc (size);
|
|
+ if (initrd_cmd == NULL)
|
|
+ goto finish;
|
|
+
|
|
+ tmp = grub_stpcpy (initrd_cmd, "initrd");
|
|
+ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
|
|
+ {
|
|
+ grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
|
|
+ tmp = grub_stpcpy (tmp, " ");
|
|
+ tmp = grub_stpcpy (tmp, initrd_list[i]);
|
|
+ }
|
|
+ tmp = grub_stpcpy (tmp, "\n");
|
|
+ }
|
|
+
|
|
+ finish:
|
|
+ grub_free (initrd_list);
|
|
+ return initrd_cmd;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function returns a string with the command to load a device tree blob
|
|
+ * from the BLS config file.
|
|
+ */
|
|
+static char *
|
|
+bls_get_devicetree (grub_blsuki_entry_t *entry)
|
|
+{
|
|
+ char *dt_path;
|
|
+ char *dt_cmd = NULL;
|
|
+ char *tmp;
|
|
+ grub_size_t size;
|
|
+
|
|
+ dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL));
|
|
+ if (dt_path != NULL)
|
|
+ {
|
|
+ if (grub_add (sizeof ("devicetree "), grub_strlen (dt_path), &size) ||
|
|
+ grub_add (size, 1, &size))
|
|
+ {
|
|
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ dt_cmd = grub_malloc (size);
|
|
+ if (dt_cmd == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ tmp = dt_cmd;
|
|
+ tmp = grub_stpcpy (dt_cmd, "devicetree ");
|
|
+ tmp = grub_stpcpy (tmp, dt_path);
|
|
+ tmp = grub_stpcpy (tmp, "\n");
|
|
+ }
|
|
+
|
|
+ return dt_cmd;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function puts together all of the commands generated from the contents
|
|
+ * of the BLS config file and creates a new entry in the GRUB boot menu.
|
|
+ */
|
|
+static void
|
|
+bls_create_entry (grub_blsuki_entry_t *entry)
|
|
+{
|
|
+ int argc = 0;
|
|
+ const char **argv = NULL;
|
|
+ char *title = NULL;
|
|
+ char *linux_path = NULL;
|
|
+ char *linux_cmd = NULL;
|
|
+ char *initrd_cmd = NULL;
|
|
+ char *dt_cmd = NULL;
|
|
+ char *id = entry->filename;
|
|
+ grub_size_t id_len;
|
|
+ char *hotkey = NULL;
|
|
+ char *users = NULL;
|
|
+ char **classes = NULL;
|
|
+ char **args = NULL;
|
|
+ char *src = NULL;
|
|
+ int i;
|
|
+ grub_size_t size;
|
|
+ bool blsuki_save_default;
|
|
+
|
|
+ linux_path = blsuki_get_val (entry, "linux", NULL);
|
|
+ if (linux_path == NULL)
|
|
+ {
|
|
+ grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", entry->filename);
|
|
+ goto finish;
|
|
+ }
|
|
+
|
|
+ id_len = grub_strlen (id);
|
|
+ if (id_len >= BLS_EXT_LEN && grub_strcmp (id + id_len - BLS_EXT_LEN, ".conf") == 0)
|
|
+ id[id_len - BLS_EXT_LEN] = '\0';
|
|
+
|
|
+ title = blsuki_get_val (entry, "title", NULL);
|
|
+ hotkey = blsuki_get_val (entry, "grub_hotkey", NULL);
|
|
+ users = blsuki_expand_val (blsuki_get_val (entry, "grub_users", NULL));
|
|
+ classes = blsuki_make_list (entry, "grub_class", NULL);
|
|
+ args = blsuki_make_list (entry, "grub_arg", &argc);
|
|
+
|
|
+ argc++;
|
|
+ if (grub_mul (argc + 1, sizeof (char *), &size))
|
|
+ {
|
|
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv list"));
|
|
+ goto finish;
|
|
+ }
|
|
+
|
|
+ argv = grub_malloc (size);
|
|
+ if (argv == NULL)
|
|
+ goto finish;
|
|
+
|
|
+ argv[0] = (title != NULL) ? title : linux_path;
|
|
+ for (i = 1; i < argc; i++)
|
|
+ argv[i] = args[i - 1];
|
|
+ argv[argc] = NULL;
|
|
+
|
|
+ linux_cmd = bls_get_linux (entry);
|
|
+ if (linux_cmd == NULL)
|
|
+ goto finish;
|
|
+
|
|
+ initrd_cmd = bls_get_initrd (entry);
|
|
+ if (grub_errno != GRUB_ERR_NONE)
|
|
+ goto finish;
|
|
+
|
|
+ dt_cmd = bls_get_devicetree (entry);
|
|
+ if (grub_errno != GRUB_ERR_NONE)
|
|
+ goto finish;
|
|
+
|
|
+ blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false);
|
|
+ src = grub_xasprintf ("%s%s%s%s",
|
|
+ blsuki_save_default ? "savedefault\n" : "",
|
|
+ linux_cmd, initrd_cmd ? initrd_cmd : "",
|
|
+ dt_cmd ? dt_cmd : "");
|
|
+
|
|
+ grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry);
|
|
+
|
|
+ finish:
|
|
+ grub_free (linux_cmd);
|
|
+ grub_free (dt_cmd);
|
|
+ grub_free (initrd_cmd);
|
|
+ grub_free (classes);
|
|
+ grub_free (args);
|
|
+ grub_free (argv);
|
|
+ grub_free (src);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function fills a find_entry_info struct passed in by the info parameter.
|
|
+ * If the dirname or devid parameters are set to NULL, the dirname and devid
|
|
+ * fields in the info parameter will be set to default values. If info already
|
|
+ * has a value in the dev fields, we can compare it to the value passed in by
|
|
+ * the devid parameter or the default devid to see if we need to open a new
|
|
+ * device.
|
|
+ */
|
|
+static grub_err_t
|
|
+blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid)
|
|
+{
|
|
+ grub_device_t dev;
|
|
+ grub_fs_t fs;
|
|
+
|
|
+ if (info == NULL)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not set");
|
|
+
|
|
+ if (devid == NULL)
|
|
+ {
|
|
+ devid = grub_env_get ("root");
|
|
+ if (devid == NULL)
|
|
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root");
|
|
+ }
|
|
+
|
|
+ /* Check that we aren't closing and opening the same device. */
|
|
+ if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0)
|
|
+ {
|
|
+ grub_device_close (info->dev);
|
|
+ info->dev = NULL;
|
|
+ }
|
|
+ /* If we are using the same device, then we can skip this step and only set the directory. */
|
|
+ if (info->dev == NULL)
|
|
+ {
|
|
+ grub_dprintf ("blsuki", "opening %s\n", devid);
|
|
+ dev = grub_device_open (devid);
|
|
+ if (dev == NULL)
|
|
+ return grub_errno;
|
|
+
|
|
+ grub_dprintf ("blsuki", "probing fs\n");
|
|
+ fs = grub_fs_probe (dev);
|
|
+ if (fs == NULL)
|
|
+ {
|
|
+ grub_device_close (dev);
|
|
+ return grub_errno;
|
|
+ }
|
|
+
|
|
+ info->devid = devid;
|
|
+ info->dev = dev;
|
|
+ info->fs = fs;
|
|
+ }
|
|
+
|
|
+ info->dirname = dirname;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function searches for BLS config files based on the data in the info
|
|
+ * parameter. If the fallback option is enabled, the default location will be
|
|
+ * checked for BLS config files if the first attempt fails.
|
|
+ */
|
|
+static void
|
|
+blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
|
|
+{
|
|
+ struct read_entry_info read_entry_info;
|
|
+ grub_fs_t dir_fs = NULL;
|
|
+ grub_device_t dir_dev = NULL;
|
|
+ bool fallback = false;
|
|
+ int r;
|
|
+
|
|
+ do
|
|
+ {
|
|
+ read_entry_info.file = NULL;
|
|
+ read_entry_info.dirname = info->dirname;
|
|
+
|
|
+ grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname);
|
|
+ dir_dev = info->dev;
|
|
+ dir_fs = info->fs;
|
|
+ read_entry_info.devid = info->devid;
|
|
+
|
|
+ r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry,
|
|
+ &read_entry_info);
|
|
+ if (r != 0)
|
|
+ {
|
|
+ grub_dprintf ("blsuki", "blsuki_read_entry returned error\n");
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * If we aren't able to find BLS entries in the directory given by info->dirname,
|
|
+ * we can fallback to the default location "/boot/loader/entries/" and see if we
|
|
+ * can find the files there.
|
|
+ */
|
|
+ if (entries == NULL && fallback == false && enable_fallback == true)
|
|
+ {
|
|
+ blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
|
|
+ grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
|
|
+ read_entry_info.dirname, info->dirname);
|
|
+ fallback = true;
|
|
+ }
|
|
+ else
|
|
+ fallback = false;
|
|
+ }
|
|
+ while (fallback == true);
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+blsuki_load_entries (char *path, bool enable_fallback)
|
|
+{
|
|
+ grub_size_t len;
|
|
+ static grub_err_t r;
|
|
+ const char *devid = NULL;
|
|
+ char *dir = NULL;
|
|
+ struct find_entry_info info = {
|
|
+ .dev = NULL,
|
|
+ .fs = NULL,
|
|
+ .dirname = NULL,
|
|
+ };
|
|
+ struct read_entry_info rei = {
|
|
+ .devid = NULL,
|
|
+ .dirname = NULL,
|
|
+ };
|
|
+
|
|
+ if (path != NULL)
|
|
+ {
|
|
+ len = grub_strlen (path);
|
|
+ if (len >= BLS_EXT_LEN && grub_strcmp (path + len - BLS_EXT_LEN, ".conf") == 0)
|
|
+ {
|
|
+ rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
|
|
+ if (rei.file == NULL)
|
|
+ return grub_errno;
|
|
+
|
|
+ /* blsuki_read_entry() closes the file. */
|
|
+ return blsuki_read_entry (path, NULL, &rei);
|
|
+ }
|
|
+ else if (path[0] == '(')
|
|
+ {
|
|
+ devid = path + 1;
|
|
+
|
|
+ dir = grub_strchr (path, ')');
|
|
+ if (dir == NULL)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name `%s'"), path);
|
|
+
|
|
+ *dir = '\0';
|
|
+
|
|
+ /* Check if there is more than the devid in the path. */
|
|
+ if (dir + 1 < path + len)
|
|
+ dir = dir + 1;
|
|
+ }
|
|
+ else if (path[0] == '/')
|
|
+ dir = path;
|
|
+ }
|
|
+
|
|
+ if (dir == NULL)
|
|
+ dir = (char *) GRUB_BLS_CONFIG_PATH;
|
|
+
|
|
+ r = blsuki_set_find_entry_info (&info, dir, devid);
|
|
+ if (r == GRUB_ERR_NONE)
|
|
+ blsuki_find_entry (&info, enable_fallback);
|
|
+
|
|
+ if (info.dev != NULL)
|
|
+ grub_device_close (info.dev);
|
|
+
|
|
+ return r;
|
|
+}
|
|
+
|
|
+static bool
|
|
+blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int idx)
|
|
+{
|
|
+ const char *title;
|
|
+ const char *def_entry_end;
|
|
+ long def_idx;
|
|
+
|
|
+ if (def_entry == NULL || *def_entry == '\0')
|
|
+ return false;
|
|
+
|
|
+ if (grub_strcmp (def_entry, entry->filename) == 0)
|
|
+ return true;
|
|
+
|
|
+ title = blsuki_get_val (entry, "title", NULL);
|
|
+
|
|
+ if (title != NULL && grub_strcmp (def_entry, title) == 0)
|
|
+ return true;
|
|
+
|
|
+ def_idx = grub_strtol (def_entry, &def_entry_end, 0);
|
|
+ if (*def_entry_end != '\0' || def_idx < 0 || def_idx > GRUB_INT_MAX)
|
|
+ return false;
|
|
+
|
|
+ if ((int) def_idx == idx)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * This function creates a GRUB boot menu entry for each BLS entry in the
|
|
+ * entries list.
|
|
+ */
|
|
+static grub_err_t
|
|
+blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id)
|
|
+{
|
|
+ const char *def_entry = NULL;
|
|
+ grub_blsuki_entry_t *entry = NULL;
|
|
+ int idx = 0;
|
|
+
|
|
+ def_entry = grub_env_get ("default");
|
|
+
|
|
+ FOR_BLSUKI_ENTRIES(entry)
|
|
+ {
|
|
+ if (entry->visible == true)
|
|
+ {
|
|
+ idx++;
|
|
+ continue;
|
|
+ }
|
|
+ if ((show_default == true && blsuki_is_default_entry (def_entry, entry, idx) == true) ||
|
|
+ (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) ||
|
|
+ (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0))
|
|
+ {
|
|
+ bls_create_entry (entry);
|
|
+ entry->visible = true;
|
|
+ }
|
|
+
|
|
+ idx++;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
|
|
+ char **args __attribute__ ((unused)))
|
|
+{
|
|
+ grub_err_t err;
|
|
+ struct grub_arg_list *state = ctxt->state;
|
|
+ char *path = NULL;
|
|
+ char *entry_id = NULL;
|
|
+ bool enable_fallback = false;
|
|
+ bool show_default = false;
|
|
+ bool show_non_default = false;
|
|
+ bool all = true;
|
|
+ entries = NULL;
|
|
+
|
|
+ if (state[0].set)
|
|
+ path = state[0].arg;
|
|
+ if (state[1].set)
|
|
+ enable_fallback = true;
|
|
+ if (state[2].set)
|
|
+ {
|
|
+ show_default = true;
|
|
+ all = false;
|
|
+ }
|
|
+ if (state[3].set)
|
|
+ {
|
|
+ show_non_default = true;
|
|
+ all = false;
|
|
+ }
|
|
+ if (state[4].set)
|
|
+ {
|
|
+ entry_id = state[4].arg;
|
|
+ all = false;
|
|
+ }
|
|
+ if (all == true)
|
|
+ {
|
|
+ show_default = true;
|
|
+ show_non_default = true;
|
|
+ }
|
|
+
|
|
+ err = blsuki_load_entries (path, enable_fallback);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+
|
|
+ return blsuki_create_entries (show_default, show_non_default, entry_id);
|
|
+}
|
|
+
|
|
+static grub_extcmd_t bls_cmd;
|
|
+
|
|
+GRUB_MOD_INIT(blsuki)
|
|
+{
|
|
+ bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0,
|
|
+ N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"),
|
|
+ N_("Import Boot Loader Specification snippets."),
|
|
+ bls_opt);
|
|
+}
|
|
+
|
|
+GRUB_MOD_FINI(blsuki)
|
|
+{
|
|
+ grub_unregister_extcmd (bls_cmd);
|
|
+}
|
|
diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
|
|
index ff0b8a6b9b..ab0db07037 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, NULL, NULL);
|
|
+ entrysrc, 0, NULL, NULL, NULL);
|
|
grub_free (args);
|
|
entrysrc[0] = 0;
|
|
grub_free (oldname);
|
|
@@ -205,7 +205,7 @@ legacy_file (const char *filename)
|
|
args[0] = entryname;
|
|
grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
|
|
NULL, NULL, entrysrc, 0, NULL,
|
|
- NULL);
|
|
+ NULL, NULL);
|
|
grub_free (args);
|
|
}
|
|
|
|
diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
|
|
index b175a1b43b..d6cf329203 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 *index, struct bls_entry *bls)
|
|
+ int submenu, int *index, struct bls_entry *bls, grub_blsuki_entry_t *blsuki)
|
|
{
|
|
int menu_hotkey = 0;
|
|
char **menu_args = NULL;
|
|
@@ -196,6 +196,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
|
|
(*last)->sourcecode = menu_sourcecode;
|
|
(*last)->submenu = submenu;
|
|
(*last)->bls = bls;
|
|
+ (*last)->blsuki = blsuki;
|
|
|
|
menu->size++;
|
|
if (index)
|
|
@@ -297,7 +298,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
|
|
ctxt->state[2].arg, 0,
|
|
ctxt->state[3].arg,
|
|
ctxt->extcmd->cmd->name[0] == 's',
|
|
- NULL, NULL);
|
|
+ NULL, NULL, NULL);
|
|
|
|
src = args[argc - 1];
|
|
args[argc - 1] = NULL;
|
|
@@ -315,7 +316,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
|
|
users,
|
|
ctxt->state[2].arg, prefix, src + 1,
|
|
ctxt->extcmd->cmd->name[0] == 's', NULL,
|
|
- NULL);
|
|
+ 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 dd20e51290..e18a494dd8 100644
|
|
--- a/grub-core/normal/main.c
|
|
+++ b/grub-core/normal/main.c
|
|
@@ -75,6 +75,10 @@ grub_normal_free_menu (grub_menu_t menu)
|
|
{
|
|
entry->bls->visible = 0;
|
|
}
|
|
+ if (entry->blsuki)
|
|
+ {
|
|
+ entry->blsuki->visible = 0;
|
|
+ }
|
|
|
|
grub_free ((void *) entry->id);
|
|
grub_free ((void *) entry->users);
|
|
diff --git a/include/grub/menu.h b/include/grub/menu.h
|
|
index 0acdc2aa6b..43a3c5809b 100644
|
|
--- a/include/grub/menu.h
|
|
+++ b/include/grub/menu.h
|
|
@@ -20,6 +20,7 @@
|
|
#ifndef GRUB_MENU_HEADER
|
|
#define GRUB_MENU_HEADER 1
|
|
|
|
+
|
|
struct bls_entry
|
|
{
|
|
struct bls_entry *next;
|
|
@@ -30,6 +31,18 @@ struct bls_entry
|
|
int visible;
|
|
};
|
|
|
|
+struct grub_blsuki_entry
|
|
+{
|
|
+ struct grub_blsuki_entry *next;
|
|
+ struct grub_blsuki_entry **prev;
|
|
+ struct keyval **keyvals;
|
|
+ grub_size_t keyvals_size;
|
|
+ int nkeyvals;
|
|
+ char *filename;
|
|
+ bool visible;
|
|
+};
|
|
+typedef struct grub_blsuki_entry grub_blsuki_entry_t;
|
|
+
|
|
struct grub_menu_entry_class
|
|
{
|
|
char *name;
|
|
@@ -73,6 +86,7 @@ struct grub_menu_entry
|
|
|
|
/* BLS used to populate the entry */
|
|
struct bls_entry *bls;
|
|
+ grub_blsuki_entry_t *blsuki;
|
|
};
|
|
typedef struct grub_menu_entry *grub_menu_entry_t;
|
|
|
|
diff --git a/include/grub/normal.h b/include/grub/normal.h
|
|
index 8839ad85a1..6d2b59db88 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 *index, struct bls_entry *bls);
|
|
+ int submenu, int *index, struct bls_entry *bls, grub_blsuki_entry_t *blsuki);
|
|
|
|
grub_err_t
|
|
grub_normal_set_password (const char *user, const char *password);
|