You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1609 lines
39 KiB
1609 lines
39 KiB
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);
|
|
|