diff --git a/zipl/include/scan.h b/zipl/include/scan.h index f2c7b1b0..0fe415c9 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 8dc6b6db..2aed54fb 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 873d6804..98919759 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 . If none is supplied, the environment variable ZIPLCONF is evaluated if set, otherwise /etc/zipl.conf is used. +.TP +.BR "\-b " " or " "\-\-blsdir=" +Use the specified to parse BootLoaderSpec config files. +If none is supplied, the /boot/loader/entries directory is used. + .TP .BR "\-t " " or " "\-\-target=" Use the specified . diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5 index 8c454cd0..d4877d88 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 9d5f00b6..e6d29818 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 adb288e0..fe72e9ab 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 +#include #include #include #include #include #include +#include + #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 1eaa82b9..65eefdb2 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",