6aab78b451
Add patches to support population IPL sections using BootLoaderSpec files instead of having them defined in the zipl.conf file. This allows to have a static zipl.conf file, and update the boot entries by just dropping BLS fragments files in a directory. In this configuration the grubby tool isn't used, and instead all needed tasks (copy kernel, generate an initramfs, copy BLS configs, etc) should be made by kernel-install scripts. Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
412 lines
12 KiB
Diff
412 lines
12 KiB
Diff
From 4d3fb60159566dfb011a855d899425e972ed951a Mon Sep 17 00:00:00 2001
|
|
From: Javier Martinez Canillas <javierm@redhat.com>
|
|
Date: Wed, 2 May 2018 12:26:58 +0200
|
|
Subject: [PATCH] zipl: Add BootLoaderSpec support
|
|
|
|
The BootLoaderSpec (BLS) defines a file format for boot configurations,
|
|
so bootloaders can parse these files and create their boot menu entries
|
|
by using the information provided by them [0].
|
|
|
|
This allow to configure the boot items as drop-in files in a directory
|
|
instead of having to parse and modify a bootloader configuration file.
|
|
|
|
If the /boot/loader/entries exists and there are BLS files there, then
|
|
these are parsed and configuration sections are added without the need
|
|
to have these in a zipl.conf file.
|
|
|
|
A different BLS directory can be specified from the command line using
|
|
the --blsdir option.
|
|
|
|
[0]: https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/
|
|
|
|
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
|
|
---
|
|
zipl/include/scan.h | 1 +
|
|
zipl/include/zipl.h | 1 +
|
|
zipl/man/zipl.8 | 12 ++-
|
|
zipl/man/zipl.conf.5 | 21 +++++
|
|
zipl/src/job.c | 24 +++++-
|
|
zipl/src/scan.c | 179 +++++++++++++++++++++++++++++++++++++++++++
|
|
zipl/src/zipl.c | 1 +
|
|
7 files changed, 237 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/zipl/include/scan.h b/zipl/include/scan.h
|
|
index f2c7b1b094a..0fe415c9b2d 100644
|
|
--- a/zipl/include/scan.h
|
|
+++ b/zipl/include/scan.h
|
|
@@ -119,6 +119,7 @@ extern enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM];
|
|
|
|
|
|
int scan_file(const char* filename, struct scan_token** array);
|
|
+int scan_bls(const char* blsdir, struct scan_token** token, int scan_size);
|
|
void scan_free(struct scan_token* array);
|
|
char* scan_keyword_name(enum scan_keyword_id id);
|
|
int scan_check_defaultboot(struct scan_token* scan);
|
|
diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h
|
|
index 8dc6b6db1a5..2aed54fb2b5 100644
|
|
--- a/zipl/include/zipl.h
|
|
+++ b/zipl/include/zipl.h
|
|
@@ -43,6 +43,7 @@
|
|
|
|
#define ZIPL_CONF_VAR "ZIPLCONF"
|
|
#define ZIPL_DEFAULT_CONF "/etc/zipl.conf"
|
|
+#define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries"
|
|
|
|
#define MENU_DEFAULT_PROMPT 0
|
|
#define MENU_DEFAULT_TIMEOUT 0
|
|
diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8
|
|
index 873d680465a..989197590e4 100644
|
|
--- a/zipl/man/zipl.8
|
|
+++ b/zipl/man/zipl.8
|
|
@@ -25,7 +25,7 @@ loading a data file to initialize named saved segments (NSS)
|
|
Each of these operations is characterized by a boot configuration, i.e. a
|
|
set of required parameters.
|
|
.B zipl
|
|
-supports two ways of specifying a boot configuration:
|
|
+supports three ways of specifying a boot configuration:
|
|
.IP " -"
|
|
.B command line:
|
|
all parameters are provided through the command line switches described below.
|
|
@@ -37,6 +37,11 @@ parameters are provided by sections defined in a configuration file (see
|
|
Using a configuration file, you can either specify a single boot configuration
|
|
or a menu, i.e. a list of configurations from which users can choose at boot
|
|
time.
|
|
+.IP " -"
|
|
+.B BLS config files:
|
|
+boot configurations are specified using BootLoaderSpec (BLS) configuration files. The
|
|
+.BR zipl.conf (5)
|
|
+configuration file is still used to specify parameters or aditional boot configurations.
|
|
.PP
|
|
|
|
To use a single boot configuration section, provide its name as parameter to
|
|
@@ -117,6 +122,11 @@ Print version information, then exit.
|
|
Use the specified <CONFIG FILE>. If none is supplied, the environment
|
|
variable ZIPLCONF is evaluated if set, otherwise /etc/zipl.conf is used.
|
|
|
|
+.TP
|
|
+.BR "\-b <BLS DIRECTORY>" " or " "\-\-blsdir=<BLS DIRECTORY>"
|
|
+Use the specified <BLS DIRECTORY> to parse BootLoaderSpec config files.
|
|
+If none is supplied, the /boot/loader/entries directory is used.
|
|
+
|
|
.TP
|
|
.BR "\-t <TARGET DIRECTORY>" " or " "\-\-target=<TARGET DIRECTORY>"
|
|
Use the specified <TARGET DIRECTORY>.
|
|
diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5
|
|
index 8c454cd01e6..d4877d884fc 100644
|
|
--- a/zipl/man/zipl.conf.5
|
|
+++ b/zipl/man/zipl.conf.5
|
|
@@ -117,6 +117,27 @@ timeout = 0
|
|
.br
|
|
.PP
|
|
|
|
+.B BootLoaderSpec configuration files
|
|
+
|
|
+Another way to specify IPL sections is to use BLS config files. These are
|
|
+configuration files located by default at /boot/loader/entries, but another
|
|
+location can be specified using the '\-\-blsdir' option of zipl.
|
|
+
|
|
+.IP
|
|
+# An example for a BLS configuration file
|
|
+.br
|
|
+
|
|
+version 4.15.9
|
|
+.br
|
|
+linux /vmlinuz-4.15.9
|
|
+.br
|
|
+initrd /initramfs-4.15.9
|
|
+.br
|
|
+options root=/dev/dasda1 console=ttyS0
|
|
+.PP
|
|
+
|
|
+The location of the linux and initrd has to be specified relative to the boot partition. The BLS config files are only used to specify the IPL sections, a zipl.conf configuration files is still needed for global parameters.
|
|
+
|
|
.B Boot menu
|
|
|
|
The
|
|
diff --git a/zipl/src/job.c b/zipl/src/job.c
|
|
index 9d5f00b6bdc..e6d298188d5 100644
|
|
--- a/zipl/src/job.c
|
|
+++ b/zipl/src/job.c
|
|
@@ -27,6 +27,7 @@
|
|
/* Command line options */
|
|
static struct option options[] = {
|
|
{ "config", required_argument, NULL, 'c'},
|
|
+ { "blsdir", required_argument, NULL, 'b'},
|
|
{ "target", required_argument, NULL, 't'},
|
|
{ "targetbase", required_argument, NULL, 0xaa},
|
|
{ "targettype", required_argument, NULL, 0xab},
|
|
@@ -55,11 +56,12 @@ static struct option options[] = {
|
|
};
|
|
|
|
/* Command line option abbreviations */
|
|
-static const char option_string[] = "-c:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:";
|
|
+static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:";
|
|
|
|
struct command_line {
|
|
char* data[SCAN_KEYWORD_NUM];
|
|
char* config;
|
|
+ char *blsdir;
|
|
char* menu;
|
|
char* section;
|
|
int help;
|
|
@@ -197,6 +199,14 @@ get_command_line(int argc, char* argv[], struct command_line* line)
|
|
} else
|
|
cmdline.config = optarg;
|
|
break;
|
|
+ case 'b':
|
|
+ if (cmdline.blsdir != NULL) {
|
|
+ error_reason("Option 'blsdir' specified more "
|
|
+ "than once");
|
|
+ rc = -1;
|
|
+ } else
|
|
+ cmdline.blsdir = optarg;
|
|
+ break;
|
|
case 'm':
|
|
if (cmdline.menu != NULL) {
|
|
error_reason("Option 'menu' specified more "
|
|
@@ -1731,6 +1741,7 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
|
|
struct scan_token* scan;
|
|
struct scan_token* new_scan;
|
|
char* filename;
|
|
+ char *blsdir;
|
|
char* source;
|
|
int rc, scan_size;
|
|
|
|
@@ -1755,6 +1766,17 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
|
|
error_text("Config file '%s'", filename);
|
|
return scan_size;
|
|
}
|
|
+ /* Check if a BLS directory was provided on command line */
|
|
+ if (cmdline->blsdir != NULL) {
|
|
+ blsdir = cmdline->blsdir;
|
|
+ } else {
|
|
+ blsdir = ZIPL_DEFAULT_BLSDIR;
|
|
+ }
|
|
+ rc = scan_bls(blsdir, &scan, scan_size);
|
|
+ if (rc) {
|
|
+ error_text("BLS parsing '%s'", blsdir);
|
|
+ return rc;
|
|
+ }
|
|
if ((cmdline->menu == NULL) && (cmdline->section == NULL)) {
|
|
rc = scan_check_defaultboot(scan);
|
|
if (rc < 0) {
|
|
diff --git a/zipl/src/scan.c b/zipl/src/scan.c
|
|
index adb288e0626..fe72e9ab13d 100644
|
|
--- a/zipl/src/scan.c
|
|
+++ b/zipl/src/scan.c
|
|
@@ -16,13 +16,21 @@
|
|
#define __USE_ISOC99
|
|
#endif
|
|
|
|
+/* Need GNU function strverscmp() in dirent.h */
|
|
+#ifndef _GNU_SOURCE
|
|
+#define _GNU_SOURCE
|
|
+#endif
|
|
+
|
|
#include <ctype.h>
|
|
+#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
+#include <sys/stat.h>
|
|
+
|
|
#include "lib/util_base.h"
|
|
|
|
#include "boot.h"
|
|
@@ -633,6 +641,177 @@ scan_file(const char* filename, struct scan_token** token)
|
|
}
|
|
|
|
|
|
+static int
|
|
+bls_filter(const struct dirent *ent)
|
|
+{
|
|
+ int offset = strlen(ent->d_name) - strlen(".conf");
|
|
+
|
|
+ if (offset < 0)
|
|
+ return 0;
|
|
+
|
|
+ return strncmp(ent->d_name + offset, ".conf", strlen(".conf")) == 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+bls_sort(const struct dirent **ent_a, const struct dirent **ent_b)
|
|
+{
|
|
+ return strverscmp((*ent_a)->d_name, (*ent_b)->d_name);
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+scan_append_section_heading(struct scan_token* scan, int* index, char* name);
|
|
+static int
|
|
+scan_append_keyword_assignment(struct scan_token* scan, int* index,
|
|
+ enum scan_keyword_id id, char* value);
|
|
+
|
|
+
|
|
+static int
|
|
+scan_bls_field(struct misc_file_buffer *file, struct scan_token* scan,
|
|
+ int* index)
|
|
+{
|
|
+ int current;
|
|
+ int key_start, key_end;
|
|
+ int val_start, val_end;
|
|
+ char *val;
|
|
+
|
|
+ for (key_start = file->pos; ; file->pos++) {
|
|
+ current = misc_get_char(file, 0);
|
|
+
|
|
+ if (isblank(current)) {
|
|
+ key_end = file->pos;
|
|
+ skip_blanks(file);
|
|
+ val_start = file->pos;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!isalnum(current) && current != '_' && current != '-')
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ for (; ; file->pos++) {
|
|
+ current = misc_get_char(file, 0);
|
|
+
|
|
+ if (current == '\n' || current == EOF)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ val_end = file->pos;
|
|
+ file->buffer[key_end] = '\0';
|
|
+ file->buffer[val_end] = '\0';
|
|
+
|
|
+ if (strncmp("version", &file->buffer[key_start], key_end - key_start) == 0) {
|
|
+ scan_append_section_heading(scan, index, &file->buffer[val_start]);
|
|
+ }
|
|
+
|
|
+ if (strncmp("linux", &file->buffer[key_start], key_end - key_start) == 0) {
|
|
+ misc_asprintf(&val, "%s", &file->buffer[val_start]);
|
|
+ scan_append_keyword_assignment(scan, index, scan_keyword_image, val);
|
|
+ free(val);
|
|
+ }
|
|
+
|
|
+ if (strncmp("options", &file->buffer[key_start], key_end - key_start) == 0) {
|
|
+ scan_append_keyword_assignment(scan, index, scan_keyword_parameters,
|
|
+ &file->buffer[val_start]);
|
|
+ }
|
|
+
|
|
+ if (strncmp("initrd", &file->buffer[key_start], key_end - key_start) == 0) {
|
|
+ misc_asprintf(&val, "%s", &file->buffer[val_start]);
|
|
+ scan_append_keyword_assignment(scan, index, scan_keyword_ramdisk, val);
|
|
+ free(val);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+int
|
|
+scan_bls(const char* blsdir, struct scan_token** token, int scan_size)
|
|
+{
|
|
+ int count = 0;
|
|
+ int size, remaining = 0, n, current, rc = -1;
|
|
+ struct scan_token* buffer;
|
|
+ struct scan_token* array = *token;
|
|
+ struct dirent** bls_entries;
|
|
+ struct misc_file_buffer file;
|
|
+ struct stat sb;
|
|
+ char filename[PATH_MAX];
|
|
+
|
|
+ if (!(stat(blsdir, &sb) == 0 && S_ISDIR(sb.st_mode)))
|
|
+ return 0;
|
|
+
|
|
+ n = scandir(blsdir, &bls_entries, bls_filter, bls_sort);
|
|
+ if (n <= 0)
|
|
+ return n;
|
|
+
|
|
+ while (array[count].id != 0)
|
|
+ count++;
|
|
+
|
|
+ remaining = scan_size - count;
|
|
+
|
|
+ if (remaining < n) {
|
|
+ size = scan_size - remaining + n;
|
|
+ buffer = (struct scan_token *)misc_malloc(size * sizeof(struct scan_token));
|
|
+ if (!buffer)
|
|
+ goto err;
|
|
+ memset(buffer, 0, size * sizeof(struct scan_token));
|
|
+ memcpy(buffer, array, count * sizeof(struct scan_token));
|
|
+ } else {
|
|
+ buffer = array;
|
|
+ }
|
|
+
|
|
+ while (n--) {
|
|
+ sprintf(filename, "%s/%s", blsdir, bls_entries[n]->d_name);
|
|
+ printf("Using BLS config file '%s'\n", filename);
|
|
+
|
|
+ rc = misc_get_file_buffer(filename, &file);
|
|
+ if (rc)
|
|
+ goto err;
|
|
+
|
|
+ while ((size_t)file.pos < file.length) {
|
|
+ current = misc_get_char(&file, 0);
|
|
+ switch (current) {
|
|
+ case '#':
|
|
+ file.pos++;
|
|
+ skip_line(&file);
|
|
+ break;
|
|
+ case EOF:
|
|
+ break;
|
|
+ case '\t':
|
|
+ case '\0':
|
|
+ case ' ':
|
|
+ file.pos++;
|
|
+ break;
|
|
+ default:
|
|
+ rc = scan_bls_field(&file, buffer, &count);
|
|
+ if (rc) {
|
|
+ error_reason("Incorrect BLS field in "
|
|
+ "config file %s\n", filename);
|
|
+ goto err;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ misc_free_file_buffer(&file);
|
|
+ free(bls_entries[n]);
|
|
+ }
|
|
+
|
|
+ *token = buffer;
|
|
+ rc = 0;
|
|
+err:
|
|
+ if (n > 0) {
|
|
+ do {
|
|
+ free(bls_entries[n]);
|
|
+ } while (n-- > 0);
|
|
+ }
|
|
+
|
|
+ free(bls_entries);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
/* Search scanned tokens SCAN for a section/menu heading (according to
|
|
* TYPE) of the given NAME, beginning at token OFFSET. Return the index of
|
|
* the section/menu heading on success, a negative value on error. */
|
|
diff --git a/zipl/src/zipl.c b/zipl/src/zipl.c
|
|
index 1eaa82b9f43..65eefdb2d99 100644
|
|
--- a/zipl/src/zipl.c
|
|
+++ b/zipl/src/zipl.c
|
|
@@ -54,6 +54,7 @@ static const char* usage_text[] = {
|
|
"-h, --help Print this help, then exit",
|
|
"-v, --version Print version information, then exit",
|
|
"-c, --config CONFIGFILE Read configuration from CONFIGFILE",
|
|
+"-b, --blsdir BLSDIR Parse BootLoaderSpec files from BLSDIR",
|
|
"-t, --target TARGETDIR Write bootmap file to TARGETDIR and install",
|
|
" bootloader on device containing TARGETDIR",
|
|
" --targetbase BASEDEVICE Install bootloader on BASEDEVICE",
|
|
--
|
|
2.17.0
|
|
|