mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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:
committed by
Jan Höppner
parent
e04acab0ed
commit
d280e5b1fa
@@ -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_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);
|
void scan_free(struct scan_token* array);
|
||||||
char* scan_keyword_name(enum scan_keyword_id id);
|
char* scan_keyword_name(enum scan_keyword_id id);
|
||||||
int scan_check_defaultboot(struct scan_token* scan);
|
int scan_check_defaultboot(struct scan_token* scan);
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
#define ZIPL_CONF_VAR "ZIPLCONF"
|
#define ZIPL_CONF_VAR "ZIPLCONF"
|
||||||
#define ZIPL_DEFAULT_CONF "/etc/zipl.conf"
|
#define ZIPL_DEFAULT_CONF "/etc/zipl.conf"
|
||||||
|
#define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries"
|
||||||
|
|
||||||
#define MENU_DEFAULT_PROMPT 0
|
#define MENU_DEFAULT_PROMPT 0
|
||||||
#define MENU_DEFAULT_TIMEOUT 0
|
#define MENU_DEFAULT_TIMEOUT 0
|
||||||
|
|||||||
@@ -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
|
Each of these operations is characterized by a boot configuration, i.e. a
|
||||||
set of required parameters.
|
set of required parameters.
|
||||||
.B zipl
|
.B zipl
|
||||||
supports two ways of specifying a boot configuration:
|
supports three ways of specifying a boot configuration:
|
||||||
.IP " -"
|
.IP " -"
|
||||||
.B command line:
|
.B command line:
|
||||||
all parameters are provided through the command line switches described below.
|
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
|
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
|
or a menu, i.e. a list of configurations from which users can choose at boot
|
||||||
time.
|
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
|
.PP
|
||||||
|
|
||||||
To use a single boot configuration section, provide its name as parameter to
|
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
|
Use the specified <CONFIG FILE>. If none is supplied, the environment
|
||||||
variable ZIPLCONF is evaluated if set, otherwise /etc/zipl.conf is used.
|
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
|
.TP
|
||||||
.BR "\-t <TARGET DIRECTORY>" " or " "\-\-target=<TARGET DIRECTORY>"
|
.BR "\-t <TARGET DIRECTORY>" " or " "\-\-target=<TARGET DIRECTORY>"
|
||||||
Use the specified <TARGET DIRECTORY>.
|
Use the specified <TARGET DIRECTORY>.
|
||||||
|
|||||||
@@ -117,6 +117,27 @@ timeout = 0
|
|||||||
.br
|
.br
|
||||||
.PP
|
.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
|
.B Boot menu
|
||||||
|
|
||||||
The
|
The
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
/* Command line options */
|
/* Command line options */
|
||||||
static struct option options[] = {
|
static struct option options[] = {
|
||||||
{ "config", required_argument, NULL, 'c'},
|
{ "config", required_argument, NULL, 'c'},
|
||||||
|
{ "blsdir", required_argument, NULL, 'b'},
|
||||||
{ "target", required_argument, NULL, 't'},
|
{ "target", required_argument, NULL, 't'},
|
||||||
{ "targetbase", required_argument, NULL, 0xaa},
|
{ "targetbase", required_argument, NULL, 0xaa},
|
||||||
{ "targettype", required_argument, NULL, 0xab},
|
{ "targettype", required_argument, NULL, 0xab},
|
||||||
@@ -55,11 +56,12 @@ static struct option options[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Command line option abbreviations */
|
/* 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 {
|
struct command_line {
|
||||||
char* data[SCAN_KEYWORD_NUM];
|
char* data[SCAN_KEYWORD_NUM];
|
||||||
char* config;
|
char* config;
|
||||||
|
char *blsdir;
|
||||||
char* menu;
|
char* menu;
|
||||||
char* section;
|
char* section;
|
||||||
int help;
|
int help;
|
||||||
@@ -197,6 +199,14 @@ get_command_line(int argc, char* argv[], struct command_line* line)
|
|||||||
} else
|
} else
|
||||||
cmdline.config = optarg;
|
cmdline.config = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'b':
|
||||||
|
if (cmdline.blsdir != NULL) {
|
||||||
|
error_reason("Option 'blsdir' specified more "
|
||||||
|
"than once");
|
||||||
|
rc = -1;
|
||||||
|
} else
|
||||||
|
cmdline.blsdir = optarg;
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
if (cmdline.menu != NULL) {
|
if (cmdline.menu != NULL) {
|
||||||
error_reason("Option 'menu' specified more "
|
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* scan;
|
||||||
struct scan_token* new_scan;
|
struct scan_token* new_scan;
|
||||||
char* filename;
|
char* filename;
|
||||||
|
char *blsdir;
|
||||||
char* source;
|
char* source;
|
||||||
int rc, scan_size;
|
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);
|
error_text("Config file '%s'", filename);
|
||||||
return scan_size;
|
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)) {
|
if ((cmdline->menu == NULL) && (cmdline->section == NULL)) {
|
||||||
rc = scan_check_defaultboot(scan);
|
rc = scan_check_defaultboot(scan);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
|||||||
179
zipl/src/scan.c
179
zipl/src/scan.c
@@ -16,13 +16,21 @@
|
|||||||
#define __USE_ISOC99
|
#define __USE_ISOC99
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Need GNU function strverscmp() in dirent.h */
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "lib/util_base.h"
|
#include "lib/util_base.h"
|
||||||
|
|
||||||
#include "boot.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
|
/* Search scanned tokens SCAN for a section/menu heading (according to
|
||||||
* TYPE) of the given NAME, beginning at token OFFSET. Return the index of
|
* TYPE) of the given NAME, beginning at token OFFSET. Return the index of
|
||||||
* the section/menu heading on success, a negative value on error. */
|
* the section/menu heading on success, a negative value on error. */
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ static const char* usage_text[] = {
|
|||||||
"-h, --help Print this help, then exit",
|
"-h, --help Print this help, then exit",
|
||||||
"-v, --version Print version information, then exit",
|
"-v, --version Print version information, then exit",
|
||||||
"-c, --config CONFIGFILE Read configuration from CONFIGFILE",
|
"-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",
|
"-t, --target TARGETDIR Write bootmap file to TARGETDIR and install",
|
||||||
" bootloader on device containing TARGETDIR",
|
" bootloader on device containing TARGETDIR",
|
||||||
" --targetbase BASEDEVICE Install bootloader on BASEDEVICE",
|
" --targetbase BASEDEVICE Install bootloader on BASEDEVICE",
|
||||||
|
|||||||
Reference in New Issue
Block a user