diff --git a/NEWS b/NEWS
index 77f0e76ba3..1dfa06a0d7 100644
--- a/NEWS
+++ b/NEWS
@@ -21,13 +21,42 @@ CHANGES WITH 251:
for breaking backward compatibility instead, as nobody should be
affected, given the state of the current interface.
- * Service monitor environment variables will only be passed to OnFailure=/OnSuccess=
- handlers if exactly one unit lists the handler unit as OnFailure=/OnSuccess=.
- Therefore, $MONITOR_METADATA is no longer used, and instead separate
- variables are used: $MONITOR_SERVICE_RESULT, $MONITOR_EXIT_CODE,
- $MONITOR_EXIT_STATUS, $MONITOR_INVOCATION_ID and $MONITOR_UNIT.
- For cases when a single handler needs to watch multiple units, use a
- templated handler.
+ * Service monitor environment variables will only be passed to
+ OnFailure=/OnSuccess= handlers if exactly one unit lists the handler
+ unit as OnFailure=/OnSuccess=. Therefore, $MONITOR_METADATA is no
+ longer used, and instead separate variables are used:
+ $MONITOR_SERVICE_RESULT, $MONITOR_EXIT_CODE, $MONITOR_EXIT_STATUS,
+ $MONITOR_INVOCATION_ID and $MONITOR_UNIT. For cases when a single
+ handler needs to watch multiple units, use a templated handler.
+
+ * kernel-install's and bootctl's Boot Loader Specification Type #1
+ entry generation logic has been reworked. The user may now pick
+ explicitly by which "token" string to name the installation's boot
+ entries, through the new /etc/kernel/entry-token file or the new
+ --entry-token= switch to bootctl. By default — as before — the
+ entries are named after the local machine ID. However, in "golden
+ image" environments, where the machine ID shall be initialized on
+ first boot (as opposed to at installation time before first boot) the
+ machine ID is not be available at build time to name the entry
+ after. In this case the the --entry-token= switch to bootctl (or the
+ /etc/kernel/entry-token file) may be used to override the "token" to
+ identify the entry by, and use another ID, for example the IMAGE_ID=
+ or ID= fields from /etc/os-release. This will make the OS images
+ independent of any machine ID, and ensure that the images will not
+ carry any identifiable information before first boot, but on the
+ other hand means that multiple parallel installations of the very
+ same image on the same disk cannot be supported. Summary: if you are
+ building golden images that shall acquire identity information
+ exclusively on first boot, make sure to both remove /etc/machine-id
+ *and* to write /etc/kernel/entry-token to the value of the IMAGE_ID
+ or ID field of /etc/os-release or another suitable identifier before
+ deploying the image.
+
+ * The --make-machine-id-directory= switch to bootctl has been replaced
+ by --make-entry-directory=, given that the entry directory is not
+ necessarily named after the machine ID, but after some other suitable
+ ID as selected via --entry-token= described above. The old name of
+ the option is still understood to maximize compatibility.
* Services with Restart=always and a failing ExecCondition= will no longer
be restarted, to bring ExecCondition= in line with Condition*= settings.
diff --git a/man/bootctl.xml b/man/bootctl.xml
index 16c4985796..ff49a8043c 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -258,23 +258,60 @@
-
- Control creation and deletion of the top-level machine ID directory on the file
- system containing boot loader entries (i.e. beneath the file system returned by the
- option, see above) during and
- , respectively. Defaults to no. See
+
+ Controls creation and deletion of the Boot Loader Specification Type #1 entry
+ directory on the file system containing resources such as kernel images and initial RAM disk images
+ during and , respectively. The directory is named
+ after the entry token, as specified with parameter described below,
+ and is placed immediately below the $BOOT root directory (i.e. beneath the file
+ system returned by the option, see above). Defaults to
+ no.
+
+
+
+
+
+ Controls how to name and identify boot loader entries for this OS
+ installation. Accepted during , and takes one of auto,
+ machine-id, os-id, os-image-id or an
+ arbitrary string prefixed by literal: as argument.
+
+ If set to the entries are named after the machine ID of the running
+ system (e.g. b0e793a9baf14b5fa13ecbe84ff637ac). See
machine-id5 for
details about the machine ID concept and file.
- Overriding this may be desirable to hide the machine ID from the (unencrypted) ESP, configure a
- kernel-install8
- script, or, conversely, commit a transient machine ID.
+ If set to the entries are named after the OS ID of the running system,
+ i.e. the ID= field of
+ os-release5
+ (e.g. fedora). Similar, if set to the entries are
+ named after the OS image ID of the running system, i.e. the IMAGE_ID= field of
+ os-release (e.g. vendorx-cashier-system).
- The top-level machine ID directory is useful to allow smooth multi-boot installations: each
- installed OS instance will have a different machine ID and thus a separate directory to place its
- boot-time resources in. If this feature is turned off with this option, care needs to be taken that
- multiple OS instances do not place conflicting files on the shared ESP and Extended Boot Loader
- Partitions, or that multiple OS instances are not possible.
+ If set to (the default), the /etc/kernel/entry-token
+ file will be read if it exists, and the stored value used. Otherwise if the local machine ID is
+ initialized it is used. Otherwise IMAGE_ID= from os-release
+ will be used, if set. Otherwise, ID= from os-release will be
+ used, if set.
+
+ Unless set to machine-id, or when
+ is used the selected token string is written to a file
+ /etc/kernel/entry-token, to ensure it will be used for future entries. This file
+ is also read by
+ kernel-install8,
+ in order to identify under which name to generate boot loader entries for newly installed kernels, or
+ to determine the entry names for removing old ones.
+
+ Using the machine ID for naming the entries is generally preferable, however there are cases
+ where using the other identifiers is a good option. Specifically: if the identification data that the
+ machine ID entails shall not be stored on the (unencrypted) $BOOT partition, or if
+ the ID shall be generated on first boot and is not known when the entries are prepared. Note that
+ using the machine ID has the benefit that multiple parallel installations of the same OS can coexist
+ on the same medium, and they can update their boot loader entries independently. When using another
+ identifier (such as the OS ID or the OS image ID), parallel installations of the same OS would try to
+ use the same entry name. To support parallel installations, the installer must use a different entry
+ token when adding a second installation.
diff --git a/man/kernel-install.xml b/man/kernel-install.xml
index 685617863e..5ec76999ad 100644
--- a/man/kernel-install.xml
+++ b/man/kernel-install.xml
@@ -39,21 +39,21 @@
/boot/, /efi/, or /boot/efi/, see below.
- kernel-install will execute the files
- located in the directory /usr/lib/kernel/install.d/
- and the local administration directory /etc/kernel/install.d/.
- All files are collectively sorted and executed in lexical order, regardless of the directory in
- which they live. However, files with identical filenames replace each other.
- Files in /etc/kernel/install.d/ take precedence over files with the same name
- in /usr/lib/kernel/install.d/. This can be used to override a system-supplied
- executables with a local file if needed; a symbolic link in /etc/kernel/install.d/
- with the same name as an executable in /usr/lib/kernel/install.d/,
- pointing to /dev/null, disables the executable entirely. Executables must have the
- extension .install; other extensions are ignored.
+ kernel-install will run the executable files ("plugins") located in the
+ directory /usr/lib/kernel/install.d/ and the local administration directory
+ /etc/kernel/install.d/. All files are collectively sorted and executed in lexical
+ order, regardless of the directory in which they live. However, files with identical filenames replace
+ each other. Files in /etc/kernel/install.d/ take precedence over files with the
+ same name in /usr/lib/kernel/install.d/. This can be used to override a
+ system-supplied executables with a local file if needed; a symbolic link in
+ /etc/kernel/install.d/ with the same name as an executable in
+ /usr/lib/kernel/install.d/, pointing to /dev/null, disables the
+ executable entirely. Executables must have the extension .install; other extensions
+ are ignored.
- An executable should return 0 on success. It may also
- return 77 to cause the whole operation to terminate
- (executables later in lexical order will be skipped).
+ An executable placed in these directories should return 0 on success. It may
+ also return 77 to cause the whole operation to terminate (executables later in
+ lexical order will be skipped).
@@ -64,37 +64,45 @@
add KERNEL-VERSIONKERNEL-IMAGE [INITRD-FILE ...]This command expects a kernel version string and a path to a kernel image file as
- arguments. kernel-install calls the executables from
- /usr/lib/kernel/install.d/*.install and
- /etc/kernel/install.d/*.install with the following arguments:
+ arguments. Optionally, one or more initial RAM disk images may be specified as well (note that
+ plugins might generate additional ones). kernel-install calls the executable
+ files from /usr/lib/kernel/install.d/*.install and
+ /etc/kernel/install.d/*.install (i.e. the plugins) with the following
+ arguments:
- add KERNEL-VERSION$BOOT/MACHINE-ID/KERNEL-VERSION/KERNEL-IMAGE [INITRD-FILE ...]
-
+ add KERNEL-VERSION$BOOT/ENTRY-TOKEN/KERNEL-VERSION/KERNEL-IMAGE [INITRD-FILE ...]
+
+ The third argument directly refers to the path where to place kernel images, initial RAM disk
+ images and other resources for Boot
+ Loader Specification Type #1 entries (the "entry directory"). If other boot loader schemes
+ are used the parameter may be ignored. The ENTRY-TOKEN string is
+ typically the machine ID and is supposed to identify the local installation on the system. For
+ details see below.Two default plugins execute the following operations in this case:kernel-install creates
- $BOOT/MACHINE-ID/KERNEL-VERSION,
- if enabled (see $KERNEL_INSTALL_LAYOUT=).
+ $BOOT/ENTRY-TOKEN/KERNEL-VERSION,
+ if enabled (see $KERNEL_INSTALL_LAYOUT).50-depmod.install runs
depmod8 for the
KERNEL-VERSION.
- 90-loaderentry.install
- copies KERNEL-IMAGE to
- $BOOT/MACHINE-ID/KERNEL-VERSION/linux.
+ 90-loaderentry.install copies
+ KERNEL-IMAGE to
+ $BOOT/ENTRY-TOKEN/KERNEL-VERSION/linux.
If INITRD-FILEs are provided, it also copies them to
- $BOOT/MACHINE-ID/KERNEL_VERSION/INITRD-FILE.
+ $BOOT/ENTRY-TOKEN/KERNEL_VERSION/INITRD-FILE.
It also creates a boot loader entry according to the Boot Loader Specification in
- $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION.conf.
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification (Type #1) in
+ $BOOT/loader/entries/ENTRY-TOKEN-KERNEL-VERSION.conf.
The title of the entry is the PRETTY_NAME parameter specified in
- /etc/os-release or /usr/lib/os-release (if the former is
- missing), or "Linux KERNEL-VERSION", if unset.
+ /etc/os-release or /usr/lib/os-release (if the former
+ is missing), or "Linux KERNEL-VERSION", if unset.
- If $KERNEL_INSTALL_LAYOUT= is not "bls", this plugin does nothing.
+ If $KERNEL_INSTALL_LAYOUT is not "bls", this plugin does nothing.
@@ -104,13 +112,13 @@
This command expects a kernel version string as single argument. This calls executables from
/usr/lib/kernel/install.d/*.install and
/etc/kernel/install.d/*.install with the following arguments:
-
- remove KERNEL-VERSION$BOOT/MACHINE-ID/KERNEL-VERSION/
- Afterwards, kernel-install removes the directory
- $BOOT/MACHINE-ID/KERNEL-VERSION/
- and its contents.
+ remove KERNEL-VERSION$BOOT/ENTRY-TOKEN/KERNEL-VERSION/
+
+ Afterwards, kernel-install removes the entry directory
+ $BOOT/ENTRY-TOKEN/KERNEL-VERSION/
+ and its contents, if it exists.Two default plugins execute the following operations in this case:
@@ -118,14 +126,18 @@
50-depmod.install removes the files generated by depmod for this kernel again.90-loaderentry.install removes the file
- $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION.conf.
-
- kernel-install removes
- $BOOT/MACHINE-ID/KERNEL-VERSION,
- if enabled (see $KERNEL_INSTALL_LAYOUT=).
+ $BOOT/loader/entries/ENTRY-TOKEN-KERNEL-VERSION.conf.
+
+ inspect
+
+ Shows the various paths and parameters configured or auto-detected. In particular shows the
+ values of the various $KERNEL_INSTALL_* environment variables listed
+ below.
+
+
@@ -135,9 +147,9 @@
The partition where the kernels and Boot
Loader Specification snippets are located is called $BOOT.
kernel-install determines the location of this partition by checking
- /efi/, /boot/, and /boot/efi/
- in turn. The first location where $BOOT/loader/entries/ or
- $BOOT/$MACHINE_ID/ exists is used.
+ /efi/, /boot/, and /boot/efi/ in turn. The
+ first location where $BOOT/loader/entries/ or
+ $BOOT/ENTRY-TOKEN/ exists is used.
@@ -163,36 +175,57 @@
If is used, $KERNEL_INSTALL_VERBOSE=1 will be set for
the plugins. They may output additional logs in this case.
- If MACHINE_ID= is set and not empty, it will be used as MACHINE-ID,
- overriding any automatic detection attempts. The value must be a valid machine ID (32 hexadecimal characters).
+ If $MACHINE_ID is set and not empty when kernel-install is
+ invoked, it will be used as MACHINE-ID, overriding any automatic detection
+ attempts. The value must be a valid machine ID (32 hexadecimal characters).
- KERNEL_INSTALL_MACHINE_ID= is set for the plugins to the desired MACHINE-ID –
- either 32 hexadecimal characters or the special value Default.
- KERNEL_INSTALL_BOOT_ROOT= is set for the plugins to the root directory (mount point, usually) of the hierarchy
- where boot-loader entries, kernel images, and associated resources should be placed. Can be overridden by setting BOOT_ROOT=.
+ $KERNEL_INSTALL_MACHINE_ID is set for the plugins to the desired
+ MACHINE-ID to use. It's always a 128bit ID, and typically the ID from
+ /etc/machine-id or the one passed in via $MACHINE_ID. (If no
+ machine ID was specified via these methods it might be generated randomly by
+ kernel-install, in which case it only applies to this invocation.)
- KERNEL_INSTALL_LAYOUT=bls|other|... is set for the plugins to specify the installation layout.
- Defaults to if $BOOT/MACHINE-ID exists, or otherwise.
- Additional layout names may be defined by convention. If a plugin uses a special layout,
- it's encouraged to declare its own layout name and configure layout= in install.conf upon initial installation.
+ $KERNEL_INSTALL_ENTRY_TOKEN is set for the plugins to the desired entry "token"
+ to use. It's an identifier that shall be used to identify the local installation, and is often the
+ machine ID, i.e. same as $KERNEL_INSTALL_MACHINE_ID, but might also be a different
+ type of identifier, for example a fixed string or the ID=,
+ IMAGE_ID= values from /etc/os-release. The string passed here
+ will be used to name Boot Loader Specification entries, or the directories the kernel image and initial
+ RAM disk images are placed into. Note that while oftentimes
+ $KERNEL_INSTALL_ENTRY_TOKEN and $KERNEL_INSTALL_MACHINE_ID are set
+ to the same value, the latter is guaranteed to be a valid 32 character ID in lowercase hexadecimals while
+ the former can be any short string. The entry token to use is read from
+ /etc/kernel/entry-token, if it exists. Otherwise a few possible candidates below the
+ $BOOT are searched for Boot Loader Specification Type 1 entry directories, and if
+ found the entry token is derived from that. If that is not successful the machine ID is used as
+ fallback.
- KERNEL_INSTALL_INITRD_GENERATOR=... is set for plugins to select the initrd generator.
- This should be configured as initrd_generator= in install.conf.
-
+ $KERNEL_INSTALL_BOOT_ROOT is set for the plugins to the absolute path of the
+ root directory (mount point, usually) of the hierarchy where boot loader entries, kernel images, and
+ associated resources should be placed. This usually is the path where the XBOOTLDR partition or the ESP
+ (EFI System Partition) are mounted, and also conceptually referred to as $BOOT. Can be
+ overridden by setting $BOOT_ROOT.
- 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
- on the file name and extension.
+ $KERNEL_INSTALL_LAYOUT=bls|other|... is set for the plugins to specify the
+ installation layout. Defaults to if
+ $BOOT/ENTRY-TOKEN exists, or
+ otherwise. Additional layout names may be defined by convention. If a plugin uses a special layout, it's
+ encouraged to declare its own layout name and configure layout= in
+ install.conf upon initial installation. The following values are currently
+ understood:bls
- Standard Boot Loader Specification layout,
- compatible with systemd-boot7: entries in
- $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION[+TRIES].conf,
- kernel and initrds under $BOOT/MACHINE-ID/KERNEL-VERSION/
- Provided by 90-loaderentry.install.
+ Standard Boot Loader
+ Specification Type #1 layout, compatible with
+ systemd-boot7:
+ entries in
+ $BOOT/loader/entries/ENTRY-TOKEN-KERNEL-VERSION[+TRIES].conf,
+ kernel and initrds under
+ $BOOT/ENTRY-TOKEN/KERNEL-VERSION/
+ Implemented by 90-loaderentry.install.
@@ -202,6 +235,15 @@
+
+ $KERNEL_INSTALL_INITRD_GENERATOR is set for plugins to select the initrd
+ generator. This should be configured as initrd_generator= in
+ install.conf.
+
+
+ $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
+ on the file name and extension.
@@ -249,25 +291,21 @@
- /etc/machine-id
+ /etc/kernel/entry-token
- The content of this file specifies the machine identification
- MACHINE-ID. If /etc/machine-id
- cannot be read or is temporary (backed by a file on tmpfs),
- kernel-install will use Default instead.
+ If this file exists it is read and used as "entry token" for this system, i.e. is used for
+ naming Boot Loader Specification entries, see
+ $KERNEL_INSTALL_ENTRY_TOKEN above for details.
- /etc/machine-info
+ /etc/machine-id
- If this file contains the KERNEL_INSTALL_MACHINE_ID variable,
- kernel-install will use it as MACHINE-ID instead of
- the contents of /etc/machine-id. If the variable is not found in
- /etc/machine-info, kernel-install will try to save the
- machine ID it uses to install to $BOOT to this file.
+ The content of this file specifies the machine identification
+ MACHINE-ID.
@@ -275,9 +313,9 @@
/etc/os-release/usr/lib/os-release
-
+ Read by 90-loaderentry.install.
- If available, PRETTY_NAME is read from these files and used as the title of the boot menu entry.
+ If available, PRETTY_NAME= is read from these files and used as the title of the boot menu entry.
Otherwise, Linux KERNEL-VERSION will be used.
diff --git a/man/machine-info.xml b/man/machine-info.xml
index 4467c59c94..fddcfb6a6c 100644
--- a/man/machine-info.xml
+++ b/man/machine-info.xml
@@ -129,17 +129,6 @@
-
- KERNEL_INSTALL_MACHINE_ID=
-
- Specifies the installation-specific installation directory
- kernel-install should use. The value must be a valid machine ID (32 hexadecimal
- characters). This would generally be the original machine-id that was used when the boot loader
- entries for this installation were first added. When not set, the current value of
- machine-id5
- will be used.
-
-
VENDOR=
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 1f4dd69d08..2035fa0d25 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -28,6 +28,7 @@
#include "glyph-util.h"
#include "main-func.h"
#include "mkdir.h"
+#include "os-util.h"
#include "pager.h"
#include "parse-argument.h"
#include "parse-util.h"
@@ -56,13 +57,22 @@ static bool arg_print_dollar_boot_path = false;
static bool arg_touch_variables = true;
static PagerFlags arg_pager_flags = 0;
static bool arg_graceful = false;
-static int arg_make_machine_id_directory = 0;
+static int arg_make_entry_directory = false; /* tri-state: < 0 for automatic logic */
static sd_id128_t arg_machine_id = SD_ID128_NULL;
static char *arg_install_layout = NULL;
+static enum {
+ ARG_ENTRY_TOKEN_MACHINE_ID,
+ ARG_ENTRY_TOKEN_OS_IMAGE_ID,
+ ARG_ENTRY_TOKEN_OS_ID,
+ ARG_ENTRY_TOKEN_LITERAL,
+ ARG_ENTRY_TOKEN_AUTO,
+} arg_entry_token_type = ARG_ENTRY_TOKEN_AUTO;
+static char *arg_entry_token = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
static const char *arg_dollar_boot_path(void) {
/* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
@@ -170,32 +180,133 @@ static int load_install_machine_id_and_layout(void) {
return 0;
}
-static int settle_install_machine_id(void) {
+static int settle_entry_token(void) {
+ int r;
+
+ switch (arg_entry_token_type) {
+
+ case ARG_ENTRY_TOKEN_AUTO: {
+ _cleanup_free_ char *buf = NULL;
+ r = read_one_line_file("/etc/kernel/entry-token", &buf);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to read /etc/kernel/entry-token: %m");
+
+ if (!isempty(buf)) {
+ free_and_replace(arg_entry_token, buf);
+ arg_entry_token_type = ARG_ENTRY_TOKEN_LITERAL;
+ } else if (sd_id128_is_null(arg_machine_id)) {
+ _cleanup_free_ char *id = NULL, *image_id = NULL;
+
+ r = parse_os_release(NULL,
+ "IMAGE_ID", &image_id,
+ "ID", &id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load /etc/os-release: %m");
+
+ if (!isempty(image_id)) {
+ free_and_replace(arg_entry_token, image_id);
+ arg_entry_token_type = ARG_ENTRY_TOKEN_OS_IMAGE_ID;
+ } else if (!isempty(id)) {
+ free_and_replace(arg_entry_token, id);
+ arg_entry_token_type = ARG_ENTRY_TOKEN_OS_ID;
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields.");
+ } else {
+ r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id));
+ if (r < 0)
+ return r;
+
+ arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID;
+ }
+
+ break;
+ }
+
+ case ARG_ENTRY_TOKEN_MACHINE_ID:
+ if (sd_id128_is_null(arg_machine_id))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set.");
+
+ r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id));
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_ENTRY_TOKEN_OS_IMAGE_ID: {
+ _cleanup_free_ char *buf = NULL;
+
+ r = parse_os_release(NULL, "IMAGE_ID", &buf);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load /etc/os-release: %m");
+
+ if (isempty(buf))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IMAGE_ID= field not set in /etc/os-release.");
+
+ free_and_replace(arg_entry_token, buf);
+ break;
+ }
+
+ case ARG_ENTRY_TOKEN_OS_ID: {
+ _cleanup_free_ char *buf = NULL;
+
+ r = parse_os_release(NULL, "ID", &buf);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load /etc/os-release: %m");
+
+ if (isempty(buf))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "ID= field not set in /etc/os-release.");
+
+ free_and_replace(arg_entry_token, buf);
+ break;
+ }
+
+ case ARG_ENTRY_TOKEN_LITERAL:
+ assert(!isempty(arg_entry_token)); /* already filled in by command line parser */
+ break;
+ }
+
+ if (isempty(arg_entry_token) || !string_is_safe(arg_entry_token))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected entry token not valid: %s", arg_entry_token);
+
+ log_debug("Using entry token: %s", arg_entry_token);
+ return 0;
+}
+
+static bool use_boot_loader_spec_type1(void) {
+ /* If the layout is not specified, or if it is set explicitly to "bls" we assume Boot Loader
+ * Specification Type #1 is the chosen format for our boot loader entries */
+ return !arg_install_layout || streq(arg_install_layout, "bls");
+}
+
+static int settle_make_entry_directory(void) {
int r;
r = load_install_machine_id_and_layout();
if (r < 0)
return r;
- bool layout_non_bls = arg_install_layout && !streq(arg_install_layout, "bls");
- if (arg_make_machine_id_directory < 0) {
- if (layout_non_bls || sd_id128_is_null(arg_machine_id))
- arg_make_machine_id_directory = 0;
- else {
- r = path_is_temporary_fs("/etc/machine-id");
- if (r < 0)
- return log_debug_errno(r, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
- arg_make_machine_id_directory = r == 0;
- }
+ r = settle_entry_token();
+ if (r < 0)
+ return r;
+
+ bool layout_type1 = use_boot_loader_spec_type1();
+ if (arg_make_entry_directory < 0) { /* Automatic mode */
+ if (layout_type1) {
+ if (arg_entry_token == ARG_ENTRY_TOKEN_MACHINE_ID) {
+ r = path_is_temporary_fs("/etc/machine-id");
+ if (r < 0)
+ return log_debug_errno(r, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
+
+ arg_make_entry_directory = r == 0;
+ } else
+ arg_make_entry_directory = true;
+ } else
+ arg_make_entry_directory = false;
}
- if (arg_make_machine_id_directory > 0 && sd_id128_is_null(arg_machine_id))
+ if (arg_make_entry_directory > 0 && !layout_type1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Machine ID not found, but bls directory creation was requested.");
-
- if (arg_make_machine_id_directory > 0 && layout_non_bls)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "KERNEL_INSTALL_LAYOUT=%s is configured, but bls directory creation was requested.",
+ "KERNEL_INSTALL_LAYOUT=%s is configured, but Boot Loader Specification Type #1 entry directory creation was requested.",
arg_install_layout);
return 0;
@@ -1039,14 +1150,14 @@ static int remove_subdirs(const char *root, const char *const *subdirs) {
return r < 0 ? r : q;
}
-static int remove_machine_id_directory(const char *root) {
+static int remove_entry_directory(const char *root) {
assert(root);
- assert(arg_make_machine_id_directory >= 0);
+ assert(arg_make_entry_directory >= 0);
- if (!arg_make_machine_id_directory || sd_id128_is_null(arg_machine_id))
+ if (!arg_make_entry_directory || !arg_entry_token)
return 0;
- return rmdir_one(root, SD_ID128_TO_STRING(arg_machine_id));
+ return rmdir_one(root, arg_entry_token);
}
static int remove_binaries(const char *esp_path) {
@@ -1138,7 +1249,7 @@ static int install_loader_config(const char *esp_path) {
const char *p;
int r;
- assert(arg_make_machine_id_directory >= 0);
+ assert(arg_make_entry_directory >= 0);
p = prefix_roota(esp_path, "/loader/loader.conf");
if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
@@ -1155,9 +1266,9 @@ static int install_loader_config(const char *esp_path) {
fprintf(f, "#timeout 3\n"
"#console-mode keep\n");
- if (arg_make_machine_id_directory) {
- assert(!sd_id128_is_null(arg_machine_id));
- fprintf(f, "default %s-*\n", SD_ID128_TO_STRING(arg_machine_id));
+ if (arg_make_entry_directory) {
+ assert(arg_entry_token);
+ fprintf(f, "default %s-*\n", arg_entry_token);
}
r = fflush_sync_and_check(f);
@@ -1174,100 +1285,33 @@ static int install_loader_config(const char *esp_path) {
return 1;
}
-static int install_machine_id_directory(const char *root) {
+static int install_entry_directory(const char *root) {
assert(root);
- assert(arg_make_machine_id_directory >= 0);
+ assert(arg_make_entry_directory >= 0);
- if (!arg_make_machine_id_directory)
+ if (!arg_make_entry_directory)
return 0;
- assert(!sd_id128_is_null(arg_machine_id));
- return mkdir_one(root, SD_ID128_TO_STRING(arg_machine_id));
+ assert(arg_entry_token);
+ return mkdir_one(root, arg_entry_token);
}
-static int install_machine_info_config(void) {
- _cleanup_free_ char *contents = NULL;
- size_t length;
- bool need_install_layout = true, need_machine_id;
+static int install_entry_token(void) {
int r;
- assert(arg_make_machine_id_directory >= 0);
+ assert(arg_make_entry_directory >= 0);
+ assert(arg_entry_token);
- /* We only want to save the machine-id if we created any directories using it. */
- need_machine_id = arg_make_machine_id_directory;
+ /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
+ * directory, or if anything else but the machine ID */
- _cleanup_fclose_ FILE *orig = fopen("/etc/machine-info", "re");
- if (!orig && errno != ENOENT)
- return log_error_errno(errno, "Failed to open /etc/machine-info: %m");
+ if (!arg_make_entry_directory && arg_entry_token_type == ARG_ENTRY_TOKEN_MACHINE_ID)
+ return 0;
- if (orig) {
- _cleanup_free_ char *install_layout = NULL, *machine_id = NULL;
-
- r = parse_env_file(orig, "/etc/machine-info",
- "KERNEL_INSTALL_LAYOUT", &install_layout,
- "KERNEL_INSTALL_MACHINE_ID", &machine_id);
- if (r < 0)
- return log_error_errno(r, "Failed to parse /etc/machine-info: %m");
-
- rewind(orig);
-
- if (!isempty(install_layout))
- need_install_layout = false;
-
- if (!isempty(machine_id))
- need_machine_id = false;
-
- if (!need_install_layout && !need_machine_id) {
- log_debug("/etc/machine-info already has KERNEL_INSTALL_MACHINE_ID=%s and KERNEL_INSTALL_LAYOUT=%s.",
- machine_id, install_layout);
- return 0;
- }
-
- r = read_full_stream(orig, &contents, &length);
- if (r < 0)
- return log_error_errno(r, "Failed to read /etc/machine-info: %m");
- }
-
- _cleanup_(unlink_and_freep) char *dst_tmp = NULL;
- _cleanup_fclose_ FILE *dst = NULL;
- r = fopen_temporary_label("/etc/machine-info", /* The path for which to the look up the label */
- "/etc/machine-info", /* Where we want the file actually to end up */
- &dst, /* The temporary file we write to */
- &dst_tmp);
+ r = write_string_file("/etc/kernel/entry-token", arg_entry_token, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
if (r < 0)
- return log_debug_errno(r, "Failed to open temporary copy of /etc/machine-info: %m");
+ return log_error_errno(r, "Failed to write entry token '%s' to /etc/kernel/entry-token", arg_entry_token);
- if (contents)
- fwrite_unlocked(contents, 1, length, dst);
-
- bool no_newline = !contents || contents[length - 1] == '\n';
-
- if (need_install_layout) {
- const char *line = "\nKERNEL_INSTALL_LAYOUT=bls\n" + no_newline;
- fwrite_unlocked(line, 1, strlen(line), dst);
- no_newline = false;
- }
-
- const char *mid_string = SD_ID128_TO_STRING(arg_machine_id);
- if (need_machine_id)
- fprintf(dst, "%sKERNEL_INSTALL_MACHINE_ID=%s\n",
- no_newline ? "" : "\n",
- mid_string);
-
- r = fflush_and_check(dst);
- if (r < 0)
- return log_error_errno(r, "Failed to write temporary copy of /etc/machine-info: %m");
- if (fchmod(fileno(dst), 0644) < 0)
- return log_debug_errno(errno, "Failed to fchmod %s: %m", dst_tmp);
-
- if (rename(dst_tmp, "/etc/machine-info") < 0)
- return log_error_errno(errno, "Failed to replace /etc/machine-info: %m");
-
- log_info("%s /etc/machine-info with%s%s%s",
- orig ? "Updated" : "Created",
- need_install_layout ? " KERNEL_INSTALL_LAYOUT=bls" : "",
- need_machine_id ? " KERNEL_INSTALL_MACHINE_ID=" : "",
- need_machine_id ? mid_string : "");
return 0;
}
@@ -1311,8 +1355,10 @@ static int help(int argc, char *argv[], void *userdata) {
" --no-pager Do not pipe output into a pager\n"
" --graceful Don't fail when the ESP cannot be found or EFI\n"
" variables cannot be written\n"
- " --make-machine-id-directory=yes|no|auto\n"
- " Create $BOOT/$MACHINE_ID\n"
+ " --make-entry-directory=yes|no|auto\n"
+ " Create $BOOT/ENTRY-TOKEN/ directory\n"
+ " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
+ " Entry token to use for this installation\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -1332,7 +1378,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_VARIABLES,
ARG_NO_PAGER,
ARG_GRACEFUL,
- ARG_MAKE_MACHINE_ID_DIRECTORY,
+ ARG_MAKE_ENTRY_DIRECTORY,
+ ARG_ENTRY_TOKEN,
};
static const struct option options[] = {
@@ -1347,7 +1394,9 @@ static int parse_argv(int argc, char *argv[]) {
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "graceful", no_argument, NULL, ARG_GRACEFUL },
- { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_MACHINE_ID_DIRECTORY },
+ { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY },
+ { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */
+ { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN },
{}
};
@@ -1405,14 +1454,40 @@ static int parse_argv(int argc, char *argv[]) {
arg_graceful = true;
break;
- case ARG_MAKE_MACHINE_ID_DIRECTORY:
- if (streq(optarg, "auto")) /* retained for backwards compatibility */
- arg_make_machine_id_directory = -1; /* yes if machine-id is permanent */
- else {
- r = parse_boolean_argument("--make-machine-id-directory=", optarg, &b);
+ case ARG_ENTRY_TOKEN: {
+ const char *e;
+
+ if (streq(optarg, "machine-id")) {
+ arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID;
+ arg_entry_token = mfree(arg_entry_token);
+ } else if (streq(optarg, "os-image-id")) {
+ arg_entry_token_type = ARG_ENTRY_TOKEN_OS_IMAGE_ID;
+ arg_entry_token = mfree(arg_entry_token);
+ } else if (streq(optarg, "os-id")) {
+ arg_entry_token_type = ARG_ENTRY_TOKEN_OS_ID;
+ arg_entry_token = mfree(arg_entry_token);
+ } else if ((e = startswith(optarg, "literal:"))) {
+ arg_entry_token_type = ARG_ENTRY_TOKEN_LITERAL;
+
+ r = free_and_strdup_warn(&arg_entry_token, e);
if (r < 0)
return r;
- arg_make_machine_id_directory = b;
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected parameter for --entry-token=: %s", optarg);
+
+ break;
+ }
+
+ case ARG_MAKE_ENTRY_DIRECTORY:
+ if (streq(optarg, "auto")) /* retained for backwards compatibility */
+ arg_make_entry_directory = -1; /* yes if machine-id is permanent */
+ else {
+ r = parse_boolean_argument("--make-entry-directory=", optarg, &b);
+ if (r < 0)
+ return r;
+
+ arg_make_entry_directory = b;
}
break;
@@ -1881,7 +1956,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = settle_install_machine_id();
+ r = settle_make_entry_directory();
if (r < 0)
return r;
@@ -1908,11 +1983,11 @@ static int verb_install(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = install_machine_id_directory(arg_dollar_boot_path());
+ r = install_entry_directory(arg_dollar_boot_path());
if (r < 0)
return r;
- r = install_machine_info_config();
+ r = install_entry_token();
if (r < 0)
return r;
@@ -1945,7 +2020,7 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = settle_install_machine_id();
+ r = settle_make_entry_directory();
if (r < 0)
return r;
@@ -1967,7 +2042,7 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
if (q < 0 && r >= 0)
r = q;
- q = remove_machine_id_directory(arg_esp_path);
+ q = remove_entry_directory(arg_esp_path);
if (q < 0 && r >= 0)
r = 1;
@@ -1977,7 +2052,7 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
if (q < 0 && r >= 0)
r = q;
- q = remove_machine_id_directory(arg_xbootldr_path);
+ q = remove_entry_directory(arg_xbootldr_path);
if (q < 0 && r >= 0)
r = q;
}
diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install
index 3edefdefb4..c1d69aa824 100644
--- a/src/kernel-install/90-loaderentry.install
+++ b/src/kernel-install/90-loaderentry.install
@@ -29,6 +29,7 @@ INITRD_OPTIONS_SHIFT=4
[ "$KERNEL_INSTALL_LAYOUT" = "bls" ] || exit 0
MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
+ENTRY_TOKEN="$KERNEL_INSTALL_ENTRY_TOKEN"
BOOT_ROOT="$KERNEL_INSTALL_BOOT_ROOT"
BOOT_MNT="$(stat -c %m "$BOOT_ROOT")"
@@ -41,10 +42,10 @@ fi
case "$COMMAND" in
remove)
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
- echo "Removing $BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION*.conf"
+ echo "Removing $BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION*.conf"
exec rm -f \
- "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" \
- "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+"*".conf"
+ "$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION.conf" \
+ "$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION+"*".conf"
;;
add)
;;
@@ -68,17 +69,26 @@ elif [ -r /usr/lib/kernel/cmdline ]; then
else
BOOT_OPTIONS="$(tr -s "$IFS" '\n' &2
exit 1
fi
- LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+$TRIES.conf"
+ LOADER_ENTRY="$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION+$TRIES.conf"
else
- LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
+ LOADER_ENTRY="$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION.conf"
fi
if ! [ -d "$ENTRY_DIR_ABS" ]; then
@@ -116,7 +126,10 @@ mkdir -p "${LOADER_ENTRY%/*}" || {
{
echo "title $PRETTY_NAME"
echo "version $KERNEL_VERSION"
- echo "machine-id $MACHINE_ID"
+ if [ "$ENTRY_TOKEN" = "$MACHINE_ID" ]; then
+ # See similar logic above for the systemd.machine_id= kernel command line option
+ echo "machine-id $MACHINE_ID"
+ fi
echo "options $BOOT_OPTIONS"
echo "linux $ENTRY_DIR/linux"
diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install
index 8cfef3208d..b8099bd12c 100755
--- a/src/kernel-install/kernel-install
+++ b/src/kernel-install/kernel-install
@@ -25,6 +25,7 @@ usage()
echo "Usage:"
echo " $0 [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE ...]"
echo " $0 [OPTIONS...] remove KERNEL-VERSION"
+ echo " $0 [OPTIONS...] inspect"
echo "Options:"
echo " -h, --help Print this help"
echo " -v, --verbose Increase verbosity"
@@ -72,13 +73,17 @@ else
[ $# -ge 1 ] && shift
fi
-if [ $# -lt 1 ]; then
- echo "Error: not enough arguments" >&2
- exit 1
-fi
+if [ "$COMMAND" = "inspect" ]; then
+ KERNEL_VERSION=""
+else
+ if [ $# -lt 1 ]; then
+ echo "Error: not enough arguments" >&2
+ exit 1
+ fi
-KERNEL_VERSION="$1"
-shift
+ KERNEL_VERSION="$1"
+ shift
+fi
layout=
initrd_generator=
@@ -89,44 +94,75 @@ elif [ -r "/usr/lib/kernel/install.conf" ]; then
. /usr/lib/kernel/install.conf
fi
-# Prefer to use an existing machine ID from /etc/machine-info or /etc/machine-id. If we're using the machine
-# ID /etc/machine-id, try to persist it in /etc/machine-info. If no machine ID is found, try to generate
-# a new machine ID in /etc/machine-info. If that fails, use "Default".
-[ -z "$MACHINE_ID" ] && [ -r /etc/machine-info ] && . /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
-[ -z "$MACHINE_ID" ] && [ -r /etc/machine-id ] && read -r MACHINE_ID >/etc/machine-info
-[ -z "$MACHINE_ID" ] && NEW_MACHINE_ID="$(systemd-id128 new)" && echo "KERNEL_INSTALL_MACHINE_ID=$NEW_MACHINE_ID" >>/etc/machine-info
-[ -z "$MACHINE_ID" ] && [ -r /etc/machine-info ] && . /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
-[ -z "$MACHINE_ID" ] && MACHINE_ID="Default"
+# If /etc/machine-id is initialized we'll use it, otherwise we'll use a freshly
+# generated one. If the user configured an explicit machine ID to use in
+# /etc/machine-info to use for our purpose, we'll use that instead (for
+# compatibility).
+[ -z "$MACHINE_ID" ] && [ -r /etc/machine-info ] && . /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
+[ -z "$MACHINE_ID" ] && [ -r /etc/machine-id ] && read -r MACHINE_ID &2; exit 1; }
+
+ ;;
*)
echo "Error: unknown command '$COMMAND'" >&2
exit 1