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-VERSION KERNEL-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