[syslinux] [PATCH] (vesa)menu.c32: Add support for BLS
Gregory Lee Bartholomew
gregory.lee.bartholomew at gmail.com
Fri May 24 15:33:02 PDT 2019
Modern distributions are moving toward a common boot scheme called "The Boot
Loader Specification". This patch enables syslinux's (vesa)menu.c32 modules to
parse the drop-in files that are defined by this new specification.
Link to The Boot Loader Specification:
https://systemd.io/BOOT_LOADER_SPECIFICATION
Link to demonstration bootdisk image (82MB gzipped):
https://drive.google.com/uc?export=download&id=1e4tQU3oM1kPOVDG3pjRGBpOXTwdeuzRx
Link to bash script used to create above bootdisk image (1.3KB gzipped):
https://drive.google.com/uc?export=download&id=1YzZYBCWFhhMtUJotzR7MLiQL6XTH_19Z
To test the bootdisk image in BIOS mode you can use:
$ qemu-system-x86_64 -machine accel=kvm -m 1024 -drive \
file=bootdisk.img,format=raw
To test the bootdisk image in UEFI mode you can use:
$ cp /usr/share/edk2/ovmf/OVMF_VARS.fd OVMF_VARS.fd
$ qemu-system-x86_64 -machine accel=kvm -m 1024 -drive \
if=pflash,format=raw,unit=0,file=/usr/share/edk2/ovmf/OVMF_CODE.fd,readonly=on \
-drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd -drive \
file=bootdisk.img,format=raw
Signed-off-by: Gregory Lee Bartholomew <gregory.lee.bartholomew at gmail.com>
---
com32/menu/readconfig.c | 340 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 340 insertions(+)
diff --git a/com32/menu/readconfig.c b/com32/menu/readconfig.c
index a433fadb..627596d8 100644
--- a/com32/menu/readconfig.c
+++ b/com32/menu/readconfig.c
@@ -21,11 +21,17 @@
#include <inttypes.h>
#include <colortbl.h>
#include <com32.h>
+#include <dirent.h>
#include <syslinux/adv.h>
#include <syslinux/config.h>
#include "menu.h"
+/* BLS entry global settings */
+#define BLS_CHUNK 16
+int bls_version_places = 3;
+char *bls_sort_by = "machine-id version title";
+
/* Empty refstring */
const char *empty_string;
@@ -1060,6 +1066,337 @@ do_include:
}
}
+/*
+ * https://systemd.io/BOOT_LOADER_SPECIFICATION
+ * #type-1-boot-loader-specification-entries
+ */
+struct blsdata {
+ const char *title;
+ const char *version;
+ char *version0; /* version string padded with zeros */
+ const char *machine_id;
+ const char *linux_;
+ const char *initrd;
+ const char *efi;
+ char *options;
+ const char *devicetree;
+ const char *architecture;
+ char *sort_field; /* used internally for sorting */
+ const char *filename;
+};
+
+static void clear_bls_data(struct blsdata *bd)
+{
+ refstr_put(bd->title);
+ refstr_put(bd->version);
+ free(bd->version0);
+ refstr_put(bd->machine_id);
+ refstr_put(bd->linux_);
+ refstr_put(bd->initrd);
+ refstr_put(bd->efi);
+ free(bd->options);
+ refstr_put(bd->devicetree);
+ refstr_put(bd->architecture);
+ free(bd->sort_field);
+ refstr_put(bd->filename);
+
+ memset(bd, 0, sizeof *bd);
+}
+
+/*
+ * Pads the numeric fields of a version string with zeros.
+ * Used to get kernel versions to sort a little better.
+ */
+static char *padver(const char *version) {
+ int i, j, d, len = strlen(version);
+ char *bwd = NULL, *fwd = NULL, *tmp;
+
+ bwd = malloc(len + 1);
+ if (!bwd)
+ goto nomem;
+
+ d = (version[len-1] >= '0' && version[len-1] <= '9') ?
+ bls_version_places : 0;
+ for (i = len, j = 0; j <= len+1; i--, j++) {
+ if (i < 0 || version[i] == '.' || version[i] == '-') {
+ if (d > 0) {
+ len += d;
+ if ((tmp = realloc(bwd, len))) {
+ bwd = tmp;
+ } else {
+ goto nomem;
+ }
+ while (d--) {
+ bwd[j++] = '0';
+ }
+ }
+ d = bls_version_places;
+ } else if (version[i] >= '0' && version[i] <= '9') {
+ d--;
+ } else if (version[i]) {
+ d = 0;
+ }
+ if (i >= 0) {
+ bwd[j] = version[i];
+ }
+ }
+
+ fwd = malloc(len + 1);
+ if (!fwd)
+ goto nomem;
+
+ tmp = bwd;
+ for (i = len; i >= 0; i--) {
+ fwd[i] = *tmp;
+ tmp++;
+ }
+ free(bwd);
+
+ return fwd;
+
+nomem:
+ dprintf("Out of memory error!\n");
+ free(bwd);
+ free(fwd);
+ return NULL;
+}
+
+static int parse_bls1_file(struct blsdata *bd, const char *filename)
+{
+ FILE *f = NULL;
+ char line[MAX_LINE], *p;
+ char *sort_field;
+
+ dprintf("Opening bls entry: %s ", filename);
+
+ f = fopen(filename, "r");
+ dprintf("%s\n", f ? "ok" : "failed");
+
+ if (!f)
+ return -1;
+
+ refstr_put(bd->filename);
+ bd->filename = refstrdup(filename);
+
+ while (fgets(line, sizeof line, f)) {
+ p = strchr(line, '\r');
+ if (p)
+ *p = '\0';
+ p = strchr(line, '\n');
+ if (p)
+ *p = '\0';
+
+ p = skipspace(line);
+
+ if (looking_at(p, "title")) {
+ refstr_put(bd->title);
+ bd->title = refstrdup(skipspace(p + 5));
+ } else if (looking_at(p, "version")) {
+ refstr_put(bd->version);
+ bd->version = refstrdup(skipspace(p + 7));
+ bd->version0 = padver(bd->version);
+ } else if (looking_at(p, "machine-id")) {
+ refstr_put(bd->machine_id);
+ bd->machine_id = refstrdup(skipspace(p + 10));
+ } else if (looking_at(p, "linux")) {
+ refstr_put(bd->linux_);
+ bd->linux_ = refstrdup(skipspace(p + 5));
+ } else if (looking_at(p, "initrd")) {
+ refstr_put(bd->initrd);
+ bd->initrd = refstrdup(skipspace(p + 6));
+ } else if (looking_at(p, "efi")) {
+ refstr_put(bd->efi);
+ bd->efi = refstrdup(skipspace(p + 3));
+ } else if (looking_at(p, "options")) {
+ /* The "options" keyword can be specified multiple times */
+ int len = bd->options ? strlen(bd->options) : 0;
+ int xlen;
+ p = skipspace(p + 7);
+ xlen = strlen(p);
+ if (len && xlen) {
+ /* Grab one space char from the file */
+ p--;
+ xlen++;
+ }
+ bd->options = realloc(bd->options, len + xlen + 1);
+ memcpy(bd->options + len, p, xlen + 1);
+ } else if (looking_at(p, "devicetree")) {
+ refstr_put(bd->devicetree);
+ bd->devicetree = refstrdup(skipspace(p + 10));
+ } else if (looking_at(p, "architecture")) {
+ refstr_put(bd->architecture);
+ bd->architecture = refstrdup(skipspace(p + 12));
+ }
+ }
+
+ fclose(f);
+
+ p = get_word(bls_sort_by, &sort_field);
+ while (sort_field && *sort_field != '\0') {
+ const char *sf = NULL;
+
+ if (looking_at(sort_field, "title")) {
+ sf = bd->title;
+ } else if (looking_at(sort_field, "version")) {
+ sf = bd->version0;
+ } else if (looking_at(sort_field, "machine-id")) {
+ sf = bd->machine_id;
+ }
+
+ if (sf) {
+ bd->sort_field = realloc(
+ bd->sort_field,
+ strlen(bd->sort_field) + strlen(sf) + 1
+ );
+ strcat(bd->sort_field, sf);
+ }
+ p = get_word(skipspace(p), &sort_field);
+ }
+
+ return (bd->linux_ || bd->efi) ? 0 : -1;
+}
+
+static int compare_blsdata(const void *p_bd1, const void *p_bd2)
+{
+ const struct blsdata *bd1 = *(const struct blsdata **)p_bd1;
+ const struct blsdata *bd2 = *(const struct blsdata **)p_bd2;
+
+ const char *a = NULL, *b = NULL;
+
+ if (bd1->sort_field && bd2->sort_field) {
+ a = bd1->sort_field;
+ b = bd2->sort_field;
+ } else if (bd1->title && bd2->title) {
+ a = bd1->title;
+ b = bd2->title;
+ } else if (bd1->linux_ && bd2->linux_) {
+ a = bd1->linux_;
+ b = bd2->linux_;
+ } else if (bd1->efi && bd2->efi) {
+ a = bd1->efi;
+ b = bd2->efi;
+ } else {
+ /* We should never get here */
+ return 0;
+ }
+
+ return strcmp(a, b);
+}
+
+static int parse_bls1_dir(const char *dirname)
+{
+ DIR *d = NULL;
+ char *filename = NULL;
+ struct dirent *de = NULL;
+ struct blsdata *nbd = NULL, **bdx = NULL;
+ int i, n_bdx = 0, n_bd = 0;
+ int rv = -1, fn_len, dn_len;
+ struct menu *m = current_menu;
+
+ if (!*dirname)
+ return -1;
+
+ dprintf("Opening bls entries directory %s ", dirname);
+
+ d = opendir(dirname);
+ dprintf("%s\n", d ? "ok" : "failed");
+
+ if (!d)
+ return -1;
+
+ dn_len = strlen(dirname);
+
+ /* Process up to 128 files */
+ /* https://wiki.syslinux.org/wiki/
+ index.php?title=Syslinux_1_Changelog#Changes_in_1.37 */
+ while ((de = readdir(d)) != NULL && n_bd < 128) {
+ if (de->d_type != DT_REG)
+ continue;
+
+ fn_len = strlen(de->d_name);
+ if (strcmp(de->d_name+(fn_len-5), ".conf"))
+ continue;
+
+ if (!(filename = malloc(dn_len + 1 + fn_len + 1)))
+ goto nomem;
+
+ strcpy(filename, dirname);
+ strcat(filename, "/");
+ strcat(filename, de->d_name);
+
+ if (n_bd >= n_bdx) {
+ struct blsdata **nbdx;
+
+ nbdx = realloc(bdx, (n_bdx + BLS_CHUNK) * sizeof *bdx);
+ if (!nbdx)
+ goto nomem;
+
+ bdx = nbdx;
+ n_bdx += BLS_CHUNK;
+ }
+
+ nbd = malloc(sizeof(struct blsdata));
+ if (!nbd)
+ goto nomem;
+
+ memset(nbd, 0, sizeof *nbd);
+ if (parse_bls1_file(nbd, filename) == 0) {
+ bdx[n_bd++] = nbd;
+ rv = 0;
+ } else {
+ clear_bls_data(nbd);
+ free(nbd);
+ }
+
+ free(filename);
+ }
+
+ closedir(d);
+
+ qsort(bdx, n_bd, sizeof *bdx, compare_blsdata);
+
+ /* For each of the BLS entries,
+ do essentially the same as the looking_at(p, "label")
+ clause of the parse_config_file() function */
+ for (i = 0; i < n_bd; i++) {
+ record(m, &ld, append);
+
+ char *label = malloc(16);
+ snprintf(label, 16, "BLS%03d", i);
+ ld.label = refstrdup(label);
+ free(label);
+
+ ld.kernel = (bdx[i]->linux_) ? refstrdup(bdx[i]->linux_) : NULL;
+ ld.type = KT_LINUX;
+ ld.passwd = NULL;
+ ld.append = (bdx[i]->options) ? refstrdup(bdx[i]->options) : NULL;
+ ld.initrd = (bdx[i]->initrd) ? refstrdup(bdx[i]->initrd) : NULL;
+ ld.menulabel = (bdx[i]->title) ? refstrdup(bdx[i]->title) : NULL;
+ ld.ipappend = ipappend;
+ ld.menudefault = ld.menuhide = ld.menuseparator =
+ ld.menudisabled = ld.menuindent = 0;
+
+ clear_bls_data(bdx[i]);
+ free(bdx[i]);
+ }
+
+ return rv;
+
+nomem:
+ dprintf("Out of memory error!\n");
+ free(filename);
+ for (i = 0; i < n_bd; i++) {
+ clear_bls_data(bdx[i]);
+ free(bdx[i]);
+ }
+ free(bdx);
+ clear_bls_data(nbd);
+ free(nbd);
+ if (d)
+ closedir(d);
+ return -1;
+}
+
static int parse_one_config(const char *filename)
{
FILE *f;
@@ -1067,6 +1404,9 @@ static int parse_one_config(const char *filename)
if (!strcmp(filename, "~"))
filename = syslinux_config_file();
+ if (!strncmp(filename, "bls1:", 5) || !strncmp(filename, "BLS1:", 5))
+ return parse_bls1_dir(filename+5);
+
dprintf("Opening config file: %s ", filename);
f = fopen(filename, "r");
--
2.20.1
More information about the Syslinux
mailing list