</varlistentry>
<varlistentry>
- <term><option>--make-machine-id-directory=yes|no</option></term>
- <listitem><para>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>--print-boot-path</option> option, see above) during <option>install</option> and
- <option>remove</option>, respectively. Defaults to <literal>no</literal>. See
+ <term><option>--make-entry-directory=yes|no</option></term>
+ <listitem><para>Controls creation and deletion of the <ulink
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> Type #1 entry
+ directory on the file system containing resources such as kernel images and initial RAM disk images
+ during <option>install</option> and <option>remove</option>, respectively. The directory is named
+ after the entry token, as specified with <option>--entry-token=</option> parameter described below,
+ and is placed immediately below the <varname>$BOOT</varname> root directory (i.e. beneath the file
+ system returned by the <option>--print-boot-path</option> option, see above). Defaults to
+ <literal>no</literal>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--entry-token=</option></term>
+
+ <listitem><para>Controls how to name and identify boot loader entries for this OS
+ installation. Accepted during <option>install</option>, and takes one of <literal>auto</literal>,
+ <literal>machine-id</literal>, <literal>os-id</literal>, <literal>os-image-id</literal> or an
+ arbitrary string prefixed by <literal>literal:</literal> as argument.</para>
+
+ <para>If set to <option>machine-id</option> the entries are named after the machine ID of the running
+ system (e.g. <literal>b0e793a9baf14b5fa13ecbe84ff637ac</literal>). See
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details about the machine ID concept and file.</para>
- <para>Overriding this may be desirable to hide the machine ID from the (unencrypted) ESP, configure a
- <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- script, or, conversely, commit a transient machine ID.</para>
-
- <para>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.</para></listitem>
+ <para>If set to <option>os-id</option> the entries are named after the OS ID of the running system,
+ i.e. the <varname>ID=</varname> field of
+ <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ (e.g. <literal>fedora</literal>). Similar, if set to <option>os-image-id</option> the entries are
+ named after the OS image ID of the running system, i.e. the <varname>IMAGE_ID=</varname> field of
+ <filename>os-release</filename> (e.g. <literal>vendorx-cashier-system</literal>).</para>
+
+ <para>If set to <option>auto</option> (the default), the <filename>/etc/kernel/entry-token</filename>
+ file will be read if it exists, and the stored value used. Otherwise if the local machine ID is
+ initialized it is used. Otherwise <varname>IMAGE_ID=</varname> from <filename>os-release</filename>
+ will be used, if set. Otherwise, <varname>ID=</varname> from <filename>os-release</filename> will be
+ used, if set.</para>
+
+ <para>Unless set to <literal>machine-id</literal>, or when
+ <option>--make-entry-directory=yes</option> is used the selected token string is written to a file
+ <filename>/etc/kernel/entry-token</filename>, to ensure it will be used for future entries. This file
+ is also read by
+ <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ 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.</para>
+
+ <para>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) <varname>$BOOT</varname> 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.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-pager"/>
#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"
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 */
return 0;
}
-static int settle_install_machine_id(void) {
+static int settle_entry_token(void) {
int r;
- r = load_install_machine_id_and_layout();
- if (r < 0)
- return r;
+ switch (arg_entry_token_type) {
- 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");
+ 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_debug_errno(r, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
- arg_make_machine_id_directory = 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;
}
- if (arg_make_machine_id_directory > 0 && sd_id128_is_null(arg_machine_id))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Machine ID not found, but bls directory creation was requested.");
+ 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;
- if (arg_make_machine_id_directory > 0 && layout_non_bls)
+ r = load_install_machine_id_and_layout();
+ if (r < 0)
+ return r;
+
+ 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_entry_directory > 0 && !layout_type1)
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;
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) {
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) */
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);
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);
-
- /* We only want to save the machine-id if we created any directories using it. */
- need_machine_id = arg_make_machine_id_directory;
-
- _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 (orig) {
- _cleanup_free_ char *install_layout = NULL, *machine_id = NULL;
+ assert(arg_make_entry_directory >= 0);
+ assert(arg_entry_token);
- 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;
+ /* 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 */
- 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");
- }
+ if (!arg_make_entry_directory && arg_entry_token_type == ARG_ENTRY_TOKEN_MACHINE_ID)
+ return 0;
- _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");
-
- 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);
+ return log_error_errno(r, "Failed to write entry token '%s' to /etc/kernel/entry-token", arg_entry_token);
- 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;
}
" --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,
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[] = {
{ "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 },
{}
};
arg_graceful = true;
break;
- case ARG_MAKE_MACHINE_ID_DIRECTORY:
+ 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;
+ } 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_machine_id_directory = -1; /* yes if machine-id is permanent */
+ arg_make_entry_directory = -1; /* yes if machine-id is permanent */
else {
- r = parse_boolean_argument("--make-machine-id-directory=", optarg, &b);
+ r = parse_boolean_argument("--make-entry-directory=", optarg, &b);
if (r < 0)
return r;
- arg_make_machine_id_directory = b;
+
+ arg_make_entry_directory = b;
}
break;
if (r < 0)
return r;
- r = settle_install_machine_id();
+ r = settle_make_entry_directory();
if (r < 0)
return r;
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;
if (r < 0)
return r;
- r = settle_install_machine_id();
+ r = settle_make_entry_directory();
if (r < 0)
return r;
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;
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;
}