diff --git a/NEWS b/NEWS
index 4677c75177..0149c0f965 100644
--- a/NEWS
+++ b/NEWS
@@ -656,15 +656,13 @@ CHANGES WITH 250:
may be used to set the boot menu time-out of the boot loader (for all
or just the subsequent boot).
- * bootctl and kernel-install will now read KERNEL_INSTALL_MACHINE_ID
- and KERNEL_INSTALL_LAYOUT from kernel/install.conf. The first
- variable specifies the machine-id to use for installation. It would
- previously be used if set in the environment, and now it'll also be
- read automatically from the config file. The second variable is new.
- When set, it specifies the layout to use for installation directories
- on the boot partition, so that tools don't need to guess it based on
- the already-existing directories. The only value that is defined
- natively is "bls", corresponding to the layout specified in
+ * bootctl and kernel-install will now read variables
+ KERNEL_INSTALL_LAYOUT= from /etc/machine-info and layout= from
+ /etc/kernel/install.conf. When set, it specifies the layout to use
+ for installation directories on the boot partition, so that tools
+ don't need to guess it based on the already-existing directories. The
+ only value that is defined natively is "bls", corresponding to the
+ layout specified in
https://systemd.io/BOOT_LOADER_SPECIFICATION/. Plugins for
kernel-install that implement a different layout can declare other
values for this variable.
diff --git a/docs/BOOT_LOADER_SPECIFICATION.md b/docs/BOOT_LOADER_SPECIFICATION.md
index 7f15a23164..375efeb1d1 100644
--- a/docs/BOOT_LOADER_SPECIFICATION.md
+++ b/docs/BOOT_LOADER_SPECIFICATION.md
@@ -309,6 +309,18 @@ focus for this specification. More specifically, on non-EFI systems
configuration snippets following this specification cannot be used to spawn
other operating systems (such as Windows).
+Unfortunately, there are implementations of boot loading infrastructure that
+are also using the /loader/entries/ directory, but place files in them that are
+not valid by this specification. In order to minimize confusion a boot loader
+implementation may place a file /loader/entries.srel next to the
+/loader/entries/ directory containing the ASCII string "type1" (suffixed
+with a UNIX newline). Tools that need to determine whether an existing
+directory implements the semantics described here may check for this file and
+contents: if it exists and contains the mentioned string, it shall assume a
+standards compliant implementation is in place. If it exists but contains a
+different string it shall assume non-standard semantics are implemented. If the
+file does not exist no assumptions should be made.
+
### Type #2 EFI Unified Kernel Images
A unified kernel image is a single EFI PE executable combining an EFI stub
diff --git a/man/kernel-install.xml b/man/kernel-install.xml
index 5ec76999ad..d6a5e43031 100644
--- a/man/kernel-install.xml
+++ b/man/kernel-install.xml
@@ -237,9 +237,8 @@
$KERNEL_INSTALL_INITRD_GENERATOR is set for plugins to select the initrd
- generator. This should be configured as initrd_generator= in
- install.conf.
-
+ generator. This may be configured as initrd_generator= in
+ install.conf. See below.
$KERNEL_INSTALL_STAGING_AREA is set for plugins to a path to a directory.
Plugins may drop files in that directory, and they will be installed as part of the loader entry, based
@@ -325,10 +324,10 @@
/etc/kernel/install.conf
- Configuration options for kernel-install,
- as a series of KEY=VALUE assignments,
- compatible with shell syntax.
- See the Environment variables section for supported keys.
+ Configuration options for kernel-install, as a series of
+ KEY=VALUE assignments, compatible with shell
+ syntax. This currently supports two keys: layout= and
+ initrd_generator=, for details see the Environment variables section above.
diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c
index cf3bbad1c4..e0a338c163 100644
--- a/src/basic/tmpfile-util.c
+++ b/src/basic/tmpfile-util.c
@@ -275,6 +275,28 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
return fd;
}
+int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) {
+ _cleanup_free_ char *path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(target);
+ assert(ret_file);
+ assert(ret_path);
+
+ fd = open_tmpfile_linkable(target, flags, &path);
+ if (fd < 0)
+ return fd;
+
+ f = take_fdopen(&fd, "w");
+ if (!f)
+ return -ENOMEM;
+
+ *ret_path = TAKE_PTR(path);
+ *ret_file = TAKE_PTR(f);
+ return 0;
+}
+
int link_tmpfile(int fd, const char *path, const char *target) {
assert(fd >= 0);
assert(target);
@@ -292,6 +314,23 @@ int link_tmpfile(int fd, const char *path, const char *target) {
return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW));
}
+int flink_tmpfile(FILE *f, const char *path, const char *target) {
+ int fd, r;
+
+ assert(f);
+ assert(target);
+
+ fd = fileno(f);
+ if (fd < 0) /* Not all FILE* objects encapsulate fds */
+ return -EBADF;
+
+ r = fflush_sync_and_check(f);
+ if (r < 0)
+ return r;
+
+ return link_tmpfile(fd, path, target);
+}
+
int mkdtemp_malloc(const char *template, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
diff --git a/src/basic/tmpfile-util.h b/src/basic/tmpfile-util.h
index 45255fc062..610cbaf87e 100644
--- a/src/basic/tmpfile-util.h
+++ b/src/basic/tmpfile-util.h
@@ -13,7 +13,9 @@ int tempfn_random_child(const char *p, const char *extra, char **ret);
int open_tmpfile_unlinkable(const char *directory, int flags);
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file);
int link_tmpfile(int fd, const char *path, const char *target);
+int flink_tmpfile(FILE *f, const char *path, const char *target);
int mkdtemp_malloc(const char *template, char **ret);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index a1d5af8c21..8103a1cf09 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -144,38 +144,70 @@ static int acquire_xbootldr(
return 1;
}
-static int load_install_machine_id_and_layout(void) {
- /* Figure out the right machine-id for operations. If KERNEL_INSTALL_MACHINE_ID is configured in
- * /etc/machine-info, let's use that. Otherwise, just use the real machine-id.
- *
- * Also load KERNEL_INSTALL_LAYOUT.
- */
+static int load_etc_machine_id(void) {
+ int r;
+
+ r = sd_id128_get_machine(&arg_machine_id);
+ if (IN_SET(r, -ENOENT, -ENOMEDIUM)) /* Not set or empty */
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to get machine-id: %m");
+
+ log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id));
+ return 0;
+}
+
+static int load_etc_machine_info(void) {
+ /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use
+ * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as
+ * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this
+ * has been deprecated and is only returned for compatibility. */
_cleanup_free_ char *s = NULL, *layout = NULL;
int r;
r = parse_env_file(NULL, "/etc/machine-info",
"KERNEL_INSTALL_LAYOUT", &layout,
"KERNEL_INSTALL_MACHINE_ID", &s);
- if (r < 0 && r != -ENOENT)
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
return log_error_errno(r, "Failed to parse /etc/machine-info: %m");
- if (isempty(s)) {
- r = sd_id128_get_machine(&arg_machine_id);
- if (r < 0 && !IN_SET(r, -ENOENT, -ENOMEDIUM))
- return log_error_errno(r, "Failed to get machine-id: %m");
- } else {
+ if (!isempty(s)) {
+ log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. Please move it to /etc/kernel/entry-token.");
+
r = sd_id128_from_string(s, &arg_machine_id);
if (r < 0)
return log_error_errno(r, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s);
+ log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=%s from KERNEL_INSTALL_MACHINE_ID in /etc/machine-info.",
+ SD_ID128_TO_STRING(arg_machine_id));
}
- log_debug("Using KERNEL_INSTALL_MACHINE_ID=%s from %s.",
- SD_ID128_TO_STRING(arg_machine_id),
- isempty(s) ? "/etc/machine_id" : "KERNEL_INSTALL_MACHINE_ID in /etc/machine-info");
if (!isempty(layout)) {
+ log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. Please move it to the layout= setting of /etc/kernel/install.conf.");
+
log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout);
- arg_install_layout = TAKE_PTR(layout);
+ free_and_replace(arg_install_layout, layout);
+ }
+
+ return 0;
+}
+
+static int load_etc_kernel_install_conf(void) {
+ _cleanup_free_ char *layout = NULL;
+ int r;
+
+ r = parse_env_file(NULL, "/etc/kernel/install.conf",
+ "layout", &layout);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse /etc/kernel/install.conf: %m");
+
+ if (!isempty(layout)) {
+ log_debug("layout=%s is specified in /etc/machine-info.", layout);
+ free_and_replace(arg_install_layout, layout);
}
return 0;
@@ -282,7 +314,15 @@ static bool use_boot_loader_spec_type1(void) {
static int settle_make_entry_directory(void) {
int r;
- r = load_install_machine_id_and_layout();
+ r = load_etc_machine_id();
+ if (r < 0)
+ return r;
+
+ r = load_etc_machine_info();
+ if (r < 0)
+ return r;
+
+ r = load_etc_kernel_install_conf();
if (r < 0)
return r;
@@ -1246,7 +1286,6 @@ static int remove_loader_variables(void) {
static int install_loader_config(const char *esp_path) {
_cleanup_(unlink_and_freep) char *t = NULL;
_cleanup_fclose_ FILE *f = NULL;
- _cleanup_close_ int fd = -1;
const char *p;
int r;
@@ -1256,13 +1295,9 @@ static int install_loader_config(const char *esp_path) {
if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
return 0;
- fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t);
- if (fd < 0)
- return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p);
-
- f = take_fdopen(&fd, "w");
- if (!f)
- return log_oom();
+ r = fopen_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t, &f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open \"%s\" for writing: %m", p);
fprintf(f, "#timeout 3\n"
"#console-mode keep\n");
@@ -1272,11 +1307,36 @@ static int install_loader_config(const char *esp_path) {
fprintf(f, "default %s-*\n", arg_entry_token);
}
- r = fflush_sync_and_check(f);
+ r = flink_tmpfile(f, t, p);
+ if (r == -EEXIST)
+ return 0; /* Silently skip creation if the file exists now (recheck) */
if (r < 0)
- return log_error_errno(r, "Failed to write \"%s\": %m", p);
+ return log_error_errno(r, "Failed to move \"%s\" into place: %m", p);
- r = link_tmpfile(fileno(f), t, p);
+ t = mfree(t);
+ return 1;
+}
+
+static int install_loader_specification(const char *root) {
+ _cleanup_(unlink_and_freep) char *t = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ p = path_join(root, "/loader/entries.srel");
+ if (!p)
+ return log_oom();
+
+ if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
+ return 0;
+
+ r = fopen_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t, &f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open \"%s\" for writing: %m", p);
+
+ fprintf(f, "type1\n");
+
+ r = flink_tmpfile(f, t, p);
if (r == -EEXIST)
return 0; /* Silently skip creation if the file exists now (recheck) */
if (r < 0)
@@ -1998,6 +2058,10 @@ static int verb_install(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
}
+
+ r = install_loader_specification(arg_dollar_boot_path());
+ if (r < 0)
+ return r;
}
(void) sync_everything();
@@ -2037,6 +2101,10 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
if (q < 0 && r >= 0)
r = q;
+ q = remove_file(arg_esp_path, "/loader/entries.srel");
+ if (q < 0 && r >= 0)
+ r = q;
+
q = remove_subdirs(arg_esp_path, esp_subdirs);
if (q < 0 && r >= 0)
r = q;
@@ -2050,7 +2118,12 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
r = q;
if (arg_xbootldr_path) {
- /* Remove the latter two also in the XBOOTLDR partition if it exists */
+ /* Remove a subset of these also from the XBOOTLDR partition if it exists */
+
+ q = remove_file(arg_xbootldr_path, "/loader/entries.srel");
+ if (q < 0 && r >= 0)
+ r = q;
+
q = remove_subdirs(arg_xbootldr_path, dollar_boot_subdirs);
if (q < 0 && r >= 0)
r = q;
diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install
index 719abf2b81..a09b998362 100755
--- a/src/kernel-install/kernel-install
+++ b/src/kernel-install/kernel-install
@@ -155,10 +155,29 @@ done
[ -z "$ENTRY_TOKEN" ] && ENTRY_TOKEN="$MACHINE_ID"
if [ -z "$layout" ]; then
- # Administrative decision: if not present, some scripts generate into /boot.
- if [ -d "$BOOT_ROOT/$ENTRY_TOKEN" ]; then
+ # No layout configured by the administrator. Let's try to figure it out
+ # automatically from metadata already contained in $BOOT_ROOT.
+ if [ -e "$BOOT_ROOT/loader/entries.srel" ]; then
+ read -r ENTRIES_SREL <"$BOOT_ROOT/loader/entries.srel"
+ if [ "$ENTRIES_SREL" = "type1" ]; then
+ # The loader/entries.srel file clearly indicates that the installed
+ # boot loader implements the proper standard upstream boot loader
+ # spec for Type #1 entries. Let's default to that, then.
+ layout="bls"
+ else
+ # The loader/entries.srel file indicates some other spec is
+ # implemented and owns the /loader/entries/ directory. Since we
+ # have no idea what that means, let's stay away from it by default.
+ layout="other"
+ fi
+ elif [ -d "$BOOT_ROOT/$ENTRY_TOKEN" ]; then
+ # If the metadata in $BOOT_ROOT doesn't tell us anything, then check if
+ # the entry token directory already exists. If so, let's assume it's
+ # the standard boot loader spec, too.
layout="bls"
else
+ # There's no metadata in $BOOT_ROOT, and apparently no entry token
+ # directory installed? Then we really don't know anything.
layout="other"
fi
fi