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/

GitHub-ID: #28
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Javier Martinez Canillas
2018-05-02 12:26:58 +02:00
committed by Jan Höppner
parent e04acab0ed
commit d280e5b1fa
7 changed files with 237 additions and 2 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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>.

View File

@@ -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

View File

@@ -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) {

View File

@@ -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. */

View File

@@ -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",