[syslinux] [PATCH] core: Add support for BLS Type 1 entries
Gregory Lee Bartholomew
gregory.lee.bartholomew at gmail.com
Tue Jul 9 12:37:34 PDT 2019
Modern distributions are moving toward a common boot scheme called "The Boot
Loader Specification". This patch enables syslinux to parse the drop-in files
that are defined by this new specification.
Link to documentation of the options added to syslinux by this patch:
https://drive.google.com/uc?export=download&id=1nuRISVJeE1whYggFURywoQFpPzc6s1MC
MD5 (syslinux-bls1.txt) = ad845afd302932edf505185b2966c10d
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=1A9psiWfR7jK316DxMRkc5iMkQ-zhEHiG
MD5 (syslinux-bls1.img.gz) = 9634058d121f80d06ae3f1af94ca0bab
Link to bash script used to create above bootdisk image:
https://drive.google.com/uc?export=download&id=1QK73PL8jJ6_lJtUnNEok7aECbzxToqEn
MD5 (syslinux-bls1.sh) = f454d9ceed648b12ab58d623ed238243
Signed-off-by: Gregory Lee Bartholomew <gregory.lee.bartholomew at gmail.com>
---
com32/elflink/ldlinux/Makefile | 2 +-
com32/elflink/ldlinux/bls.c | 230 +++++++++++++++++++++
com32/elflink/ldlinux/readconfig.c | 318 +++++++++++++++++++++++++++++
com32/include/bls.h | 44 ++++
com32/menu/Makefile | 2 +-
com32/menu/bls.c | 230 +++++++++++++++++++++
com32/menu/readconfig.c | 318 +++++++++++++++++++++++++++++
7 files changed, 1142 insertions(+), 2 deletions(-)
create mode 100644 com32/elflink/ldlinux/bls.c
create mode 100644 com32/include/bls.h
create mode 100644 com32/menu/bls.c
diff --git a/com32/elflink/ldlinux/Makefile b/com32/elflink/ldlinux/Makefile
index 87c0d362..ac4587dd 100644
--- a/com32/elflink/ldlinux/Makefile
+++ b/com32/elflink/ldlinux/Makefile
@@ -18,7 +18,7 @@ LIBS = --whole-archive $(objdir)/com32/lib/libcom32min.a
OBJS = ldlinux.o cli.o readconfig.o refstr.o colors.o getadv.o adv.o \
execute.o chainboot.o kernel.o get_key.o advwrite.o setadv.o \
- loadhigh.o msg.o
+ loadhigh.o msg.o bls.o
BTARGET = $(LDLINUX)
diff --git a/com32/elflink/ldlinux/bls.c b/com32/elflink/ldlinux/bls.c
new file mode 100644
index 00000000..16e8bf18
--- /dev/null
+++ b/com32/elflink/ldlinux/bls.c
@@ -0,0 +1,230 @@
+/*
+ * bls.c
+ *
+ * Source file for the boot loader specification
+ *
+ * https://systemd.io/BOOT_LOADER_SPECIFICATION
+ * #type-1-boot-loader-specification-entries
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dprintf.h>
+#include "refstr.h"
+#include "bls.h"
+
+const char *get_bls_field(struct blsdata *bd, const char *fieldname) {
+ if (!bd || !fieldname)
+ return NULL;
+
+ if (strcmp(fieldname, "filename") == 0)
+ return bd->filename;
+ if (strcmp(fieldname, "title") == 0)
+ return bd->title;
+ if (strcmp(fieldname, "version") == 0)
+ return bd->version;
+ if (strcmp(fieldname, "version0") == 0)
+ return bd->version0;
+ if (strcmp(fieldname, "machine-id") == 0)
+ return bd->machine_id;
+ if (strcmp(fieldname, "linux") == 0)
+ return bd->freax;
+ if (strcmp(fieldname, "initrd") == 0)
+ return bd->initrd;
+ if (strcmp(fieldname, "efi") == 0)
+ return bd->efi;
+ if (strcmp(fieldname, "options") == 0)
+ return bd->options;
+ if (strcmp(fieldname, "devicetree") == 0)
+ return bd->devicetree;
+ if (strcmp(fieldname, "architecture") == 0)
+ return bd->architecture;
+ if (strcmp(fieldname, "other") == 0)
+ return bd->other;
+
+ return NULL;
+}
+
+/*
+ * inspired by syslinux/com32/elflink/ldlinux/readconfig.c:clear_label_data
+ */
+void clear_bls_data(struct blsdata *bd)
+{
+ if (!bd)
+ return;
+
+ refstr_put(bd->filename);
+ refstr_put(bd->title);
+ refstr_put(bd->version);
+ refstr_put(bd->version0);
+ refstr_put(bd->machine_id);
+ refstr_put(bd->freax);
+ free(bd->initrd);
+ refstr_put(bd->efi);
+ free(bd->options);
+ refstr_put(bd->devicetree);
+ refstr_put(bd->architecture);
+ refstr_put(bd->other);
+ refstr_put(bd->sort_field);
+
+ memset(bd, 0, sizeof *bd);
+}
+
+/*
+ * inspired by syslinux/com32/modules/ls.c:compare_dirent
+ */
+int compare_bls_data(const void *p_bd1, const void *p_bd2)
+{
+ if (!p_bd1 || !p_bd2)
+ return 0;
+
+ 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->freax && bd2->freax) {
+ a = bd1->freax;
+ b = bd2->freax;
+ } else if (bd1->efi && bd2->efi) {
+ a = bd1->efi;
+ b = bd2->efi;
+ } else {
+ /* We should never get here */
+ return 0;
+ }
+
+ return strcmp(a, b);
+}
+
+int compare_bls_data_asc(const void *p_bd1, const void *p_bd2)
+{
+ return compare_bls_data(p_bd1, p_bd2);
+}
+
+int compare_bls_data_des(const void *p_bd1, const void *p_bd2)
+{
+ return compare_bls_data(p_bd2, p_bd1);
+}
+
+const char *format_bls_data(struct blsdata *bd, const char *fmt)
+{
+ char *ml, *tmp;
+ const char *field, *rv;
+ int i, j, ml_len;
+
+ if (!bd || !fmt)
+ return NULL;
+
+ ml = malloc(1);
+ *ml = '\0';
+ ml_len = 0;
+
+ i = 0;
+ tmp = malloc(strlen(fmt) + 1);
+ strcpy(tmp, fmt);
+ while (fmt[i]) {
+ if (fmt[i] == '$') {
+ i += 1;
+ for (j = i; isalnum(fmt[j]) || fmt[j] == '-'; j++);
+ if (j > i) {
+ tmp[j] = '\0';
+ field = get_bls_field(bd, tmp+i);
+ if (field) {
+ ml_len += strlen(field);
+ ml = realloc(ml, ml_len + 1);
+ strcat(ml, field);
+ }
+ i = j;
+ }
+ }
+ for (j = i; fmt[j] && fmt[j] != '$'; j++);
+ if (j > i) {
+ ml_len += j - i;
+ ml = realloc(ml, ml_len + 1);
+ strncat(ml, fmt+i, j - i);
+ ml[ml_len] = '\0';
+ i = j;
+ }
+ }
+ free(tmp);
+
+ rv = refstrdup(ml);
+ free(ml);
+
+ return rv;
+}
+
+/*
+ * pads the numeric fields of a version string with zeros
+ * to get kernel versions to sort a little better
+ */
+const char *padver(const char *version, const int pad)
+{
+ int i, j, p, len;
+ char *bwd = NULL, *fwd = NULL, *tmp;
+ const char *rv;
+
+ if (version == NULL || pad > 9 || pad <= 0)
+ return version;
+
+ len = strlen(version) + 1;
+ bwd = malloc(len);
+ if (!bwd)
+ goto nomem;
+
+ p = pad;
+ for (i = len-1, j = 0; j <= len; i--, j++) {
+ if (i < 0 || version[i] == '.' || version[i] == '-') {
+ if (p > 0) {
+ len += p;
+ if ((tmp = realloc(bwd, len))) {
+ bwd = tmp;
+ } else {
+ goto nomem;
+ }
+ while (p--) {
+ bwd[j++] = '0';
+ }
+ }
+ p = pad;
+ } else if (isdigit(version[i])) {
+ p--;
+ } else if (version[i]) {
+ p = 0;
+ }
+ if (i >= 0) {
+ bwd[j] = version[i];
+ }
+ }
+
+ fwd = malloc(len);
+ if (!fwd)
+ goto nomem;
+
+ tmp = bwd;
+ for (i = len-1; i >= 0; i--) {
+ fwd[i] = *tmp;
+ tmp++;
+ }
+ free(bwd);
+
+ rv = refstrdup(fwd);
+ free(fwd);
+
+ return rv;
+
+nomem:
+ dprintf("Out of memory error!\n");
+ free(bwd);
+ free(fwd);
+ return NULL;
+}
diff --git a/com32/elflink/ldlinux/readconfig.c b/com32/elflink/ldlinux/readconfig.c
index 3d6aa27e..b1397e4b 100644
--- a/com32/elflink/ldlinux/readconfig.c
+++ b/com32/elflink/ldlinux/readconfig.c
@@ -22,6 +22,7 @@
#include <inttypes.h>
#include <colortbl.h>
#include <com32.h>
+#include <dirent.h>
#include <syslinux/adv.h>
#include <syslinux/config.h>
#include <dprintf.h>
@@ -32,11 +33,22 @@
#include <syslinux/pxe_api.h>
#include "menu.h"
+#include "bls.h"
#include "config.h"
#include "getkey.h"
#include "core.h"
#include "fs.h"
+/* BLS1 entry global settings */
+char *bls1_labelf = NULL;
+char *bls1_format = NULL;
+char *bls1_sortby = NULL;
+char *bls1_pinmin = NULL;
+char *bls1_pinmax = NULL;
+int bls1_padver = BLS1_PADVER;
+int bls1_ascend = BLS1_ASCEND;
+int bls1_shwlbl = BLS1_SHWLBL;
+
const struct menu_parameter mparm[NPARAMS] = {
[P_WIDTH] = {"width", 0},
[P_MARGIN] = {"margin", 10},
@@ -641,6 +653,7 @@ extern uint16_t PXERetry;
static struct labeldata ld;
static int parse_main_config(const char *filename);
+static int parse_bls1_dir(const char *dirname);
static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
{
@@ -1239,6 +1252,40 @@ static void parse_config_file(FILE * f)
default_cmd = refstrdup(skipspace(p + 2));
}
+ else if (looking_at(p, "bls1")) {
+ p = skipspace(p + 4);
+ if (looking_at(p, "include")) {
+ p = skipspace(p + 7);
+ parse_bls1_dir((*p) ? p : BLS1_DIR);
+ } else if (looking_at(p, "labelf")) {
+ p = skipspace(p + 6);
+ bls1_labelf = realloc(bls1_labelf, strlen(p) + 1);
+ strcpy(bls1_labelf, p);
+ } else if (looking_at(p, "format")) {
+ p = skipspace(p + 6);
+ bls1_format = realloc(bls1_format, strlen(p) + 1);
+ strcpy(bls1_format, p);
+ } else if (looking_at(p, "sortby")) {
+ p = skipspace(p + 6);
+ bls1_sortby = realloc(bls1_sortby, strlen(p) + 1);
+ strcpy(bls1_sortby, p);
+ } else if (looking_at(p, "pinmin")) {
+ p = skipspace(p + 6);
+ bls1_pinmin = realloc(bls1_pinmin, strlen(p) + 1);
+ strcpy(bls1_pinmin, p);
+ } else if (looking_at(p, "pinmax")) {
+ p = skipspace(p + 6);
+ bls1_pinmax = realloc(bls1_pinmax, strlen(p) + 1);
+ strcpy(bls1_pinmax, p);
+ } else if (looking_at(p, "padver")) {
+ bls1_padver = atoi(skipspace(p + 6));
+ } else if (looking_at(p, "ascend")) {
+ bls1_ascend = atoi(skipspace(p + 6));
+ } else if (looking_at(p, "shwlbl")) {
+ bls1_shwlbl = atoi(skipspace(p + 6));
+ }
+ }
+
/*
* subset 1: pc_opencmd
* display/font/kbdmap are rather similar, open a file then do sth
@@ -1433,6 +1480,277 @@ static void parse_config_file(FILE * f)
}
}
+/*
+ * inspired by parse_config_file
+ */
+static int parse_bls1_file(struct blsdata *bd, const char *filename)
+{
+ FILE *f = NULL;
+ char line[MAX_LINE], *p, *pin;
+ const char *fmt, *tmp;
+
+ 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, bls1_padver);
+ } 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->freax);
+ bd->freax = refstrdup(skipspace(p + 5));
+ } else if (looking_at(p, "initrd")) {
+ /* The "initrd" keyword can be specified multiple times */
+ int clen = 0;
+ int xlen = 0;
+
+ p = skipspace(p + 6);
+ xlen = strlen(p);
+
+ if (xlen) {
+ if (bd->initrd) {
+ clen = strlen(bd->initrd);
+ bd->initrd[clen++] = ',';
+ }
+ bd->initrd = realloc(bd->initrd, clen + xlen + 1);
+ memcpy(bd->initrd + clen, p, xlen + 1);
+ }
+ } 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 clen = 0;
+ int xlen = 0;
+
+ p = skipspace(p + 7);
+ xlen = strlen(p);
+
+ if (xlen) {
+ if (bd->options) {
+ clen = strlen(bd->options);
+ bd->options[clen++] = ' ';
+ }
+ bd->options = realloc(bd->options, clen + xlen + 1);
+ memcpy(bd->options + clen, 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));
+ } else if (looking_at(p, "other")) {
+ refstr_put(bd->other);
+ bd->other = refstrdup(skipspace(p + 5));
+ }
+ }
+
+ fclose(f);
+
+ fmt = NULL;
+ tmp = NULL;
+
+ p = (bls1_pinmin) ? bls1_pinmin : BLS1_PINMIN;
+ pin = malloc(strlen(p) + 1);
+ strcpy(pin, p);
+ p = strchr(pin, ' ');
+ if (p) {
+ *p = '\0';
+ tmp = format_bls_data(bd, pin);
+ p++;
+ if (strstr(tmp, p))
+ fmt = (bls1_ascend) ? "0%s" : "2%s";
+ refstr_put(tmp);
+ }
+ free(pin);
+
+ p = (bls1_pinmax) ? bls1_pinmax : BLS1_PINMAX;
+ pin = malloc(strlen(p) + 1);
+ strcpy(pin, p);
+ p = strchr(pin, ' ');
+ if (p) {
+ *p = '\0';
+ tmp = format_bls_data(bd, pin);
+ p++;
+ if (strstr(tmp, p))
+ fmt = (bls1_ascend) ? "2%s" : "0%s";
+ refstr_put(tmp);
+ }
+ free(pin);
+
+ if (!fmt)
+ fmt = "1%s";
+
+ tmp = format_bls_data(bd, (bls1_sortby) ? bls1_sortby : BLS1_SORTBY);
+ refstr_put(bd->sort_field);
+ rsprintf(&bd->sort_field, fmt, tmp);
+ refstr_put(tmp);
+
+ return (bd->freax || bd->efi) ? 0 : -1;
+}
+
+/*
+ * inspired by syslinux/com32/modules/ls.c:display_directory
+ *
+ * returns the number of files that were successfully parsed,
+ * or -1 on error
+ */
+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 = 0, fn_len, dn_len;
+ struct menu *m = current_menu;
+ const char *tmp = NULL;
+
+ dprintf("Opening bls entries directory %s ", dirname);
+
+ d = opendir(dirname);
+ dprintf("%s\n", d ? "ok" : "failed");
+ if (!d)
+ return -1;
+ dn_len = strlen(dirname);
+
+ while ((de = readdir(d)) != NULL) {
+ 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;
+
+ sprintf(filename, "%s/%s", dirname, de->d_name);
+
+ if (n_bd >= n_bdx) {
+ struct blsdata **nbdx;
+
+ nbdx = realloc(bdx, (n_bdx + BLS1_CHUNK) * sizeof *bdx);
+ if (!nbdx)
+ goto nomem;
+
+ bdx = nbdx;
+ n_bdx += BLS1_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++;
+ } else {
+ clear_bls_data(nbd);
+ free(nbd);
+ }
+
+ free(filename);
+ }
+
+ closedir(d);
+
+ if (bls1_ascend) {
+ qsort(bdx, n_bd, sizeof *bdx, compare_bls_data_asc);
+ } else {
+ qsort(bdx, n_bd, sizeof *bdx, compare_bls_data_des);
+ }
+
+ /*
+ * For each of the BLS1 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);
+
+ /*
+ * labels are autonumbered
+ * with a user-configurable format
+ * that defaults to "BLS%03d"
+ */
+ rsprintf(
+ &ld.label, (bls1_labelf) ? bls1_labelf : BLS1_LABELF, i+1
+ );
+
+ if (bdx[i]->freax) {
+ ld.kernel = refstrdup(bdx[i]->freax);
+ ld.type = KT_LINUX;
+ } else {
+ ld.kernel = refstrdup(bdx[i]->efi);
+ ld.type = KT_KERNEL;
+ }
+ ld.passwd = NULL;
+ ld.append = refstrdup(bdx[i]->options);
+ ld.initrd = refstrdup(bdx[i]->initrd);
+ ld.menulabel = format_bls_data(
+ bdx[i], (bls1_format) ? bls1_format : BLS1_FORMAT
+ );
+ ld.helptext = NULL;
+ ld.ipappend = SysAppends;
+ ld.menudefault = ld.menuhide = ld.menuseparator =
+ ld.menudisabled = ld.menuindent = 0;
+
+ if (bls1_shwlbl) {
+ tmp = refstrdup(ld.menulabel);
+ refstr_put(ld.menulabel);
+ rsprintf(&ld.menulabel, "%s%s", ld.label, tmp);
+ refstr_put(tmp);
+ }
+
+ clear_bls_data(bdx[i]);
+ free(bdx[i]);
+ }
+ free(bdx);
+
+ 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_main_config(const char *filename)
{
const char *mode = "r";
diff --git a/com32/include/bls.h b/com32/include/bls.h
new file mode 100644
index 00000000..7877714b
--- /dev/null
+++ b/com32/include/bls.h
@@ -0,0 +1,44 @@
+/*
+ * bls.h
+ *
+ * Header file for boot loader specification entries
+ * https://systemd.io/BOOT_LOADER_SPECIFICATION
+ */
+
+#ifndef BLS_H
+#define BLS_H
+
+#define BLS1_DIR "/loader/entries"
+#define BLS1_CHUNK 16
+#define BLS1_LABELF "BLS%03d"
+#define BLS1_FORMAT "$title ($version)"
+#define BLS1_SORTBY "$machine-id$version0$title"
+#define BLS1_PINMIN "$filename default"
+#define BLS1_PINMAX "$filename rescue"
+#define BLS1_PADVER 3
+#define BLS1_ASCEND 0
+#define BLS1_SHWLBL 0
+
+struct blsdata {
+ const char *filename;
+ const char *title;
+ const char *version;
+ const char *version0; /* version string padded with zeros */
+ const char *machine_id;
+ const char *freax; /* "freax" because "linux" is reserved */
+ char *initrd;
+ const char *efi;
+ char *options;
+ const char *devicetree;
+ const char *architecture;
+ const char *other; /* an extra field; has no reserved purpose */
+ const char *sort_field; /* used internally for sorting */
+};
+
+void clear_bls_data(struct blsdata *);
+int compare_bls_data_asc(const void *, const void *);
+int compare_bls_data_des(const void *, const void *);
+const char *format_bls_data(struct blsdata *, const char *);
+const char *padver(const char *, const int);
+
+#endif /* BLS_H */
diff --git a/com32/menu/Makefile b/com32/menu/Makefile
index 7c2d5927..383ca6f0 100644
--- a/com32/menu/Makefile
+++ b/com32/menu/Makefile
@@ -23,7 +23,7 @@ MODULES = menu.c32 vesamenu.c32
TESTFILES =
COMMONOBJS = menumain.o readconfig.o passwd.o drain.o \
- printmsg.o colors.o background.o refstr.o
+ printmsg.o colors.o background.o refstr.o bls.o
all: $(MODULES) $(TESTFILES)
diff --git a/com32/menu/bls.c b/com32/menu/bls.c
new file mode 100644
index 00000000..16e8bf18
--- /dev/null
+++ b/com32/menu/bls.c
@@ -0,0 +1,230 @@
+/*
+ * bls.c
+ *
+ * Source file for the boot loader specification
+ *
+ * https://systemd.io/BOOT_LOADER_SPECIFICATION
+ * #type-1-boot-loader-specification-entries
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dprintf.h>
+#include "refstr.h"
+#include "bls.h"
+
+const char *get_bls_field(struct blsdata *bd, const char *fieldname) {
+ if (!bd || !fieldname)
+ return NULL;
+
+ if (strcmp(fieldname, "filename") == 0)
+ return bd->filename;
+ if (strcmp(fieldname, "title") == 0)
+ return bd->title;
+ if (strcmp(fieldname, "version") == 0)
+ return bd->version;
+ if (strcmp(fieldname, "version0") == 0)
+ return bd->version0;
+ if (strcmp(fieldname, "machine-id") == 0)
+ return bd->machine_id;
+ if (strcmp(fieldname, "linux") == 0)
+ return bd->freax;
+ if (strcmp(fieldname, "initrd") == 0)
+ return bd->initrd;
+ if (strcmp(fieldname, "efi") == 0)
+ return bd->efi;
+ if (strcmp(fieldname, "options") == 0)
+ return bd->options;
+ if (strcmp(fieldname, "devicetree") == 0)
+ return bd->devicetree;
+ if (strcmp(fieldname, "architecture") == 0)
+ return bd->architecture;
+ if (strcmp(fieldname, "other") == 0)
+ return bd->other;
+
+ return NULL;
+}
+
+/*
+ * inspired by syslinux/com32/elflink/ldlinux/readconfig.c:clear_label_data
+ */
+void clear_bls_data(struct blsdata *bd)
+{
+ if (!bd)
+ return;
+
+ refstr_put(bd->filename);
+ refstr_put(bd->title);
+ refstr_put(bd->version);
+ refstr_put(bd->version0);
+ refstr_put(bd->machine_id);
+ refstr_put(bd->freax);
+ free(bd->initrd);
+ refstr_put(bd->efi);
+ free(bd->options);
+ refstr_put(bd->devicetree);
+ refstr_put(bd->architecture);
+ refstr_put(bd->other);
+ refstr_put(bd->sort_field);
+
+ memset(bd, 0, sizeof *bd);
+}
+
+/*
+ * inspired by syslinux/com32/modules/ls.c:compare_dirent
+ */
+int compare_bls_data(const void *p_bd1, const void *p_bd2)
+{
+ if (!p_bd1 || !p_bd2)
+ return 0;
+
+ 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->freax && bd2->freax) {
+ a = bd1->freax;
+ b = bd2->freax;
+ } else if (bd1->efi && bd2->efi) {
+ a = bd1->efi;
+ b = bd2->efi;
+ } else {
+ /* We should never get here */
+ return 0;
+ }
+
+ return strcmp(a, b);
+}
+
+int compare_bls_data_asc(const void *p_bd1, const void *p_bd2)
+{
+ return compare_bls_data(p_bd1, p_bd2);
+}
+
+int compare_bls_data_des(const void *p_bd1, const void *p_bd2)
+{
+ return compare_bls_data(p_bd2, p_bd1);
+}
+
+const char *format_bls_data(struct blsdata *bd, const char *fmt)
+{
+ char *ml, *tmp;
+ const char *field, *rv;
+ int i, j, ml_len;
+
+ if (!bd || !fmt)
+ return NULL;
+
+ ml = malloc(1);
+ *ml = '\0';
+ ml_len = 0;
+
+ i = 0;
+ tmp = malloc(strlen(fmt) + 1);
+ strcpy(tmp, fmt);
+ while (fmt[i]) {
+ if (fmt[i] == '$') {
+ i += 1;
+ for (j = i; isalnum(fmt[j]) || fmt[j] == '-'; j++);
+ if (j > i) {
+ tmp[j] = '\0';
+ field = get_bls_field(bd, tmp+i);
+ if (field) {
+ ml_len += strlen(field);
+ ml = realloc(ml, ml_len + 1);
+ strcat(ml, field);
+ }
+ i = j;
+ }
+ }
+ for (j = i; fmt[j] && fmt[j] != '$'; j++);
+ if (j > i) {
+ ml_len += j - i;
+ ml = realloc(ml, ml_len + 1);
+ strncat(ml, fmt+i, j - i);
+ ml[ml_len] = '\0';
+ i = j;
+ }
+ }
+ free(tmp);
+
+ rv = refstrdup(ml);
+ free(ml);
+
+ return rv;
+}
+
+/*
+ * pads the numeric fields of a version string with zeros
+ * to get kernel versions to sort a little better
+ */
+const char *padver(const char *version, const int pad)
+{
+ int i, j, p, len;
+ char *bwd = NULL, *fwd = NULL, *tmp;
+ const char *rv;
+
+ if (version == NULL || pad > 9 || pad <= 0)
+ return version;
+
+ len = strlen(version) + 1;
+ bwd = malloc(len);
+ if (!bwd)
+ goto nomem;
+
+ p = pad;
+ for (i = len-1, j = 0; j <= len; i--, j++) {
+ if (i < 0 || version[i] == '.' || version[i] == '-') {
+ if (p > 0) {
+ len += p;
+ if ((tmp = realloc(bwd, len))) {
+ bwd = tmp;
+ } else {
+ goto nomem;
+ }
+ while (p--) {
+ bwd[j++] = '0';
+ }
+ }
+ p = pad;
+ } else if (isdigit(version[i])) {
+ p--;
+ } else if (version[i]) {
+ p = 0;
+ }
+ if (i >= 0) {
+ bwd[j] = version[i];
+ }
+ }
+
+ fwd = malloc(len);
+ if (!fwd)
+ goto nomem;
+
+ tmp = bwd;
+ for (i = len-1; i >= 0; i--) {
+ fwd[i] = *tmp;
+ tmp++;
+ }
+ free(bwd);
+
+ rv = refstrdup(fwd);
+ free(fwd);
+
+ return rv;
+
+nomem:
+ dprintf("Out of memory error!\n");
+ free(bwd);
+ free(fwd);
+ return NULL;
+}
diff --git a/com32/menu/readconfig.c b/com32/menu/readconfig.c
index a433fadb..cf77dc95 100644
--- a/com32/menu/readconfig.c
+++ b/com32/menu/readconfig.c
@@ -21,10 +21,22 @@
#include <inttypes.h>
#include <colortbl.h>
#include <com32.h>
+#include <dirent.h>
#include <syslinux/adv.h>
#include <syslinux/config.h>
#include "menu.h"
+#include "bls.h"
+
+/* BLS1 entry global settings */
+char *bls1_labelf = NULL;
+char *bls1_format = NULL;
+char *bls1_sortby = NULL;
+char *bls1_pinmin = NULL;
+char *bls1_pinmax = NULL;
+int bls1_padver = BLS1_PADVER;
+int bls1_ascend = BLS1_ASCEND;
+int bls1_shwlbl = BLS1_SHWLBL;
/* Empty refstring */
const char *empty_string;
@@ -597,6 +609,7 @@ static unsigned int ipappend = 0;
static struct labeldata ld;
static int parse_one_config(const char *filename);
+static int parse_bls1_dir(const char *dirname);
static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
{
@@ -1057,7 +1070,312 @@ do_include:
} else if (looking_at(p, "ui")) {
has_ui = 1;
}
+
+ else if (looking_at(p, "bls1")) {
+ p = skipspace(p + 4);
+ if (looking_at(p, "include")) {
+ p = skipspace(p + 7);
+ parse_bls1_dir((*p) ? p : BLS1_DIR);
+ } else if (looking_at(p, "labelf")) {
+ p = skipspace(p + 6);
+ bls1_labelf = realloc(bls1_labelf, strlen(p) + 1);
+ strcpy(bls1_labelf, p);
+ } else if (looking_at(p, "format")) {
+ p = skipspace(p + 6);
+ bls1_format = realloc(bls1_format, strlen(p) + 1);
+ strcpy(bls1_format, p);
+ } else if (looking_at(p, "sortby")) {
+ p = skipspace(p + 6);
+ bls1_sortby = realloc(bls1_sortby, strlen(p) + 1);
+ strcpy(bls1_sortby, p);
+ } else if (looking_at(p, "pinmin")) {
+ p = skipspace(p + 6);
+ bls1_pinmin = realloc(bls1_pinmin, strlen(p) + 1);
+ strcpy(bls1_pinmin, p);
+ } else if (looking_at(p, "pinmax")) {
+ p = skipspace(p + 6);
+ bls1_pinmax = realloc(bls1_pinmax, strlen(p) + 1);
+ strcpy(bls1_pinmax, p);
+ } else if (looking_at(p, "padver")) {
+ bls1_padver = atoi(skipspace(p + 6));
+ } else if (looking_at(p, "ascend")) {
+ bls1_ascend = atoi(skipspace(p + 6));
+ } else if (looking_at(p, "shwlbl")) {
+ bls1_shwlbl = atoi(skipspace(p + 6));
+ }
+ }
+ }
+}
+
+/*
+ * inspired by parse_config_file
+ */
+static int parse_bls1_file(struct blsdata *bd, const char *filename)
+{
+ FILE *f = NULL;
+ char line[MAX_LINE], *p, *pin;
+ const char *fmt, *tmp;
+
+ 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, bls1_padver);
+ } 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->freax);
+ bd->freax = refstrdup(skipspace(p + 5));
+ } else if (looking_at(p, "initrd")) {
+ /* The "initrd" keyword can be specified multiple times */
+ int clen = 0;
+ int xlen = 0;
+
+ p = skipspace(p + 6);
+ xlen = strlen(p);
+
+ if (xlen) {
+ if (bd->initrd) {
+ clen = strlen(bd->initrd);
+ bd->initrd[clen++] = ',';
+ }
+ bd->initrd = realloc(bd->initrd, clen + xlen + 1);
+ memcpy(bd->initrd + clen, p, xlen + 1);
+ }
+ } 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 clen = 0;
+ int xlen = 0;
+
+ p = skipspace(p + 7);
+ xlen = strlen(p);
+
+ if (xlen) {
+ if (bd->options) {
+ clen = strlen(bd->options);
+ bd->options[clen++] = ' ';
+ }
+ bd->options = realloc(bd->options, clen + xlen + 1);
+ memcpy(bd->options + clen, 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));
+ } else if (looking_at(p, "other")) {
+ refstr_put(bd->other);
+ bd->other = refstrdup(skipspace(p + 5));
+ }
+ }
+
+ fclose(f);
+
+ fmt = NULL;
+ tmp = NULL;
+
+ p = (bls1_pinmin) ? bls1_pinmin : BLS1_PINMIN;
+ pin = malloc(strlen(p) + 1);
+ strcpy(pin, p);
+ p = strchr(pin, ' ');
+ if (p) {
+ *p = '\0';
+ tmp = format_bls_data(bd, pin);
+ p++;
+ if (strstr(tmp, p))
+ fmt = (bls1_ascend) ? "0%s" : "2%s";
+ refstr_put(tmp);
+ }
+ free(pin);
+
+ p = (bls1_pinmax) ? bls1_pinmax : BLS1_PINMAX;
+ pin = malloc(strlen(p) + 1);
+ strcpy(pin, p);
+ p = strchr(pin, ' ');
+ if (p) {
+ *p = '\0';
+ tmp = format_bls_data(bd, pin);
+ p++;
+ if (strstr(tmp, p))
+ fmt = (bls1_ascend) ? "2%s" : "0%s";
+ refstr_put(tmp);
+ }
+ free(pin);
+
+ if (!fmt)
+ fmt = "1%s";
+
+ tmp = format_bls_data(bd, (bls1_sortby) ? bls1_sortby : BLS1_SORTBY);
+ refstr_put(bd->sort_field);
+ rsprintf(&bd->sort_field, fmt, tmp);
+ refstr_put(tmp);
+
+ return (bd->freax || bd->efi) ? 0 : -1;
+}
+
+/*
+ * inspired by syslinux/com32/modules/ls.c:display_directory
+ *
+ * returns the number of files that were successfully parsed,
+ * or -1 on error
+ */
+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 = 0, fn_len, dn_len;
+ struct menu *m = current_menu;
+ const char *tmp = NULL;
+
+ dprintf("Opening bls entries directory %s ", dirname);
+ d = opendir(dirname);
+ dprintf("%s\n", d ? "ok" : "failed");
+ if (!d)
+ return -1;
+
+ dn_len = strlen(dirname);
+
+ while ((de = readdir(d)) != NULL) {
+ 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;
+
+ sprintf(filename, "%s/%s", dirname, de->d_name);
+
+ if (n_bd >= n_bdx) {
+ struct blsdata **nbdx;
+
+ nbdx = realloc(bdx, (n_bdx + BLS1_CHUNK) * sizeof *bdx);
+ if (!nbdx)
+ goto nomem;
+
+ bdx = nbdx;
+ n_bdx += BLS1_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++;
+ } else {
+ clear_bls_data(nbd);
+ free(nbd);
+ }
+
+ free(filename);
+ }
+
+ closedir(d);
+
+ if (bls1_ascend) {
+ qsort(bdx, n_bd, sizeof *bdx, compare_bls_data_asc);
+ } else {
+ qsort(bdx, n_bd, sizeof *bdx, compare_bls_data_des);
+ }
+
+ /*
+ * For each of the BLS1 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);
+
+ /*
+ * labels are autonumbered
+ * with a user-configurable format
+ * that defaults to "BLS%03d"
+ */
+ rsprintf(
+ &ld.label, (bls1_labelf) ? bls1_labelf : BLS1_LABELF, i+1
+ );
+
+ if (bdx[i]->freax) {
+ ld.kernel = refstrdup(bdx[i]->freax);
+ ld.type = KT_LINUX;
+ } else {
+ ld.kernel = refstrdup(bdx[i]->efi);
+ ld.type = KT_KERNEL;
+ }
+ ld.passwd = NULL;
+ ld.append = refstrdup(bdx[i]->options);
+ ld.initrd = refstrdup(bdx[i]->initrd);
+ ld.menulabel = format_bls_data(
+ bdx[i], (bls1_format) ? bls1_format : BLS1_FORMAT
+ );
+ ld.helptext = NULL;
+ ld.ipappend = ipappend;
+ ld.menudefault = ld.menuhide = ld.menuseparator =
+ ld.menudisabled = ld.menuindent = 0;
+
+ if (bls1_shwlbl) {
+ tmp = refstrdup(ld.menulabel);
+ refstr_put(ld.menulabel);
+ rsprintf(&ld.menulabel, "%s%s", ld.label, tmp);
+ refstr_put(tmp);
+ }
+
+ clear_bls_data(bdx[i]);
+ free(bdx[i]);
+ }
+ free(bdx);
+
+ 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)
--
2.20.1
More information about the Syslinux
mailing list