]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
kernel-install: rewrite in C
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 14 Mar 2023 14:55:18 +0000 (23:55 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 5 Jun 2023 05:23:54 +0000 (14:23 +0900)
This is mostly a one-to-one translation of kernel-install.sh, except for
the followings:
- BOOT_ROOT is searched with find_{esp,xbootldr}_and_warn().
- entry token is searched with boot_entry_token_ensure().
- inspect command verboses more information, e.g. found plugins,
  environment variables explicitly passed to plugins, arguments passed
  to plugins.
- paths specified in $KERNEL_INSTALL_PLUGINS must be absolute.
- LC_COLLATE is set to C.UTF-8 (or any specified on build time).

By writing kernel-install C, we can share the code used by bootctl or
so, and can introduce --root and/or --image options later.

meson.build
src/kernel-install/kernel-install.c [new file with mode: 0644]
src/kernel-install/kernel-install.in [deleted file]
src/kernel-install/meson.build

index 51a5fc82e33f6e8b9252c5b9626209f515320a78..5e97284786fc7b0be83e15a96f42699cf4b732b8 100644 (file)
@@ -4376,17 +4376,17 @@ executable(
         install : true,
         install_dir : rootlibexecdir)
 
-kernel_install = custom_target(
+kernel_install = executable(
         'kernel-install',
-        input : kernel_install_in,
-        output : 'kernel-install',
-        command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+        'src/kernel-install/kernel-install.c',
+        include_directories : includes,
+        link_with : [libshared],
+        dependencies : [userspace,
+                        versiondep],
+        install_rpath : rootpkglibdir,
         install : want_kernel_install,
-        install_mode : 'rwxr-xr-x',
         install_dir : bindir)
-if want_kernel_install
-        public_programs += exe
-endif
+public_programs += kernel_install
 
 ukify = custom_target(
         'ukify',
@@ -4397,19 +4397,22 @@ ukify = custom_target(
         install_mode : 'rwxr-xr-x',
         install_dir : rootlibexecdir)
 if want_ukify
-   public_programs += ukify
+        public_programs += ukify
 endif
 
 if want_tests != 'false' and want_kernel_install
-        args = [kernel_install.full_path(), loaderentry_install, uki_copy_install]
+        args = [kernel_install.full_path(), loaderentry_install.full_path(), uki_copy_install]
+        deps = [kernel_install, loaderentry_install]
         if want_ukify and boot_stubs.length() > 0
-                args += [ukify.full_path(), ukify_install, boot_stubs[0]]
+                args += [ukify.full_path(), ukify_install.full_path(), boot_stubs[0]]
+                deps += [ukify, ukify_install, boot_stubs[0]]
         endif
 
         test('test-kernel-install',
              test_kernel_install_sh,
              env : test_env,
-             args : args)
+             args : args,
+             depends: deps)
 endif
 
 ############################################################
diff --git a/src/kernel-install/kernel-install.c b/src/kernel-install/kernel-install.c
new file mode 100644 (file)
index 0000000..de744fe
--- /dev/null
@@ -0,0 +1,1183 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+#include <stdbool.h>
+
+#include "build.h"
+#include "boot-entry.h"
+#include "chase.h"
+#include "conf-files.h"
+#include "env-file.h"
+#include "env-util.h"
+#include "exec-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "find-esp.h"
+#include "id128-util.h"
+#include "kernel-image.h"
+#include "main-func.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "rm-rf.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "verbs.h"
+
+static bool arg_verbose = false;
+
+typedef enum Action {
+        ACTION_ADD,
+        ACTION_REMOVE,
+        ACTION_INSPECT,
+        _ACTION_MAX,
+        _ACTION_INVALID = -EINVAL,
+} Action;
+
+typedef enum Layout {
+        LAYOUT_AUTO,
+        LAYOUT_UKI,
+        LAYOUT_BLS,
+        LAYOUT_OTHER,
+        _LAYOUT_MAX,
+        _LAYOUT_INVALID = -EINVAL,
+} Layout;
+
+static const char * const layout_table[_LAYOUT_MAX] = {
+        [LAYOUT_AUTO]  = "auto",
+        [LAYOUT_UKI]   = "uki",
+        [LAYOUT_BLS]   = "bls",
+        [LAYOUT_OTHER] = "other",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(layout, Layout);
+
+typedef struct Context {
+        int rfd;
+        Action action;
+        sd_id128_t machine_id;
+        bool machine_id_is_random;
+        KernelImageType kernel_image_type;
+        Layout layout;
+        char *layout_other;
+        char *conf_root;
+        char *boot_root;
+        BootEntryTokenType entry_token_type;
+        char *entry_token;
+        char *entry_dir;
+        char *version;
+        char *kernel;
+        char **initrds;
+        char *initrd_generator;
+        char *uki_generator;
+        char *staging_area;
+        char **plugins;
+        char **argv;
+        char **envp;
+} Context;
+
+static void context_done(Context *c) {
+        assert(c);
+
+        free(c->layout_other);
+        free(c->conf_root);
+        free(c->boot_root);
+        free(c->entry_token);
+        free(c->entry_dir);
+        free(c->version);
+        free(c->kernel);
+        strv_free(c->initrds);
+        free(c->initrd_generator);
+        free(c->uki_generator);
+        if (c->action == ACTION_INSPECT)
+                free(c->staging_area);
+        else
+                rm_rf_physical_and_free(c->staging_area);
+        strv_free(c->plugins);
+        strv_free(c->argv);
+        strv_free(c->envp);
+
+        safe_close(c->rfd);
+}
+
+static int context_open_root(Context *c) {
+        assert(c);
+        assert(c->rfd < 0);
+
+        c->rfd = open("/", O_CLOEXEC | O_DIRECTORY | O_PATH);
+        if (c->rfd < 0)
+                return log_error_errno(errno, "Failed to open root directory: %m");
+
+        return 0;
+}
+
+static const char* context_get_layout(const Context *c) {
+        assert(c);
+        assert(c->layout >= 0);
+
+        return c->layout_other ?: layout_to_string(c->layout);
+}
+
+static int context_set_layout(Context *c, const char *s, const char *source) {
+        Layout t;
+
+        assert(c);
+        assert(source);
+
+        if (c->layout >= 0 || !s)
+                return 0;
+
+        assert(!c->layout_other);
+
+        t = layout_from_string(s);
+        if (t >= 0)
+                c->layout = t;
+        else if (isempty(s))
+                c->layout = LAYOUT_AUTO;
+        else {
+                c->layout_other = strdup(s);
+                if (!c->layout_other)
+                        return log_oom();
+
+                c->layout = LAYOUT_OTHER;
+        }
+
+        log_debug("layout=%s set via %s", context_get_layout(c), source);
+        return 1;
+}
+
+static int context_set_machine_id(Context *c, const char *s, const char *source) {
+        int r;
+
+        assert(c);
+        assert(source);
+
+        if (!sd_id128_is_null(c->machine_id) || !s)
+                return 0;
+
+        r = sd_id128_from_string(s, &c->machine_id);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to parse machine ID specified via %s, ignoring.", source);
+
+        if (sd_id128_is_null(c->machine_id))
+                return 0;
+
+        log_debug("MACHINE_ID=%s set via %s.", SD_ID128_TO_STRING(c->machine_id), source);
+        return 1;
+}
+
+static int context_set_string(const char *s, const char *source, const char *name, char **dest) {
+        char *p;
+
+        assert(source);
+        assert(name);
+        assert(dest);
+
+        if (*dest || !s)
+                return 0;
+
+        p = strdup(s);
+        if (!p)
+                return log_oom();
+
+        log_debug("%s (%s) set via %s.", name, p, source);
+
+        *dest = p;
+        return 1;
+}
+
+static int context_set_initrd_generator(Context *c, const char *s, const char *source) {
+        assert(c);
+        return context_set_string(s, source, "INITRD_GENERATOR", &c->initrd_generator);
+}
+
+static int context_set_uki_generator(Context *c, const char *s, const char *source) {
+        assert(c);
+        return context_set_string(s, source, "UKI_GENERATOR", &c->uki_generator);
+}
+
+static int context_set_version(Context *c, const char *s) {
+        assert(c);
+
+        if (!filename_is_valid(s))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version specified: %s", s);
+
+        return context_set_string(s, "command line", "kernel version", &c->version);
+}
+
+static int context_set_path(Context *c, int rfd, const char *s, const char *source, const char *name, char **dest) {
+        char *p;
+        int r;
+
+        assert(c);
+        assert(source);
+        assert(name);
+        assert(dest);
+
+        if (*dest || !s)
+                return 0;
+
+        if (rfd >= 0)
+                r = chaseat(rfd, s, CHASE_AT_RESOLVE_IN_ROOT, &p, /* ret_fd = */ NULL);
+        else
+                r = chase(s, /* root = */ NULL, 0, &p, /* ret_fd = */ NULL);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to chase path %s for %s specified via %s, ignoring: %m",
+                                         s, name, source);
+
+        log_debug("%s (%s) set via %s.", name, p, source);
+
+        *dest = p;
+        return 1;
+}
+
+static int context_set_boot_root(Context *c, const char *s, const char *source) {
+        assert(c);
+        return context_set_path(c, c->rfd, s, source, "BOOT_ROOT", &c->boot_root);
+}
+
+static int context_set_conf_root(Context *c, const char *s, const char *source) {
+        assert(c);
+        return context_set_path(c, c->rfd, s, source, "CONF_ROOT", &c->conf_root);
+}
+
+static int context_set_kernel(Context *c, const char *s) {
+        assert(c);
+        /* The path specified via command line should be relative to CWD. */
+        return context_set_path(c, AT_FDCWD, s, "command line", "kernel image file", &c->kernel);
+}
+
+static int context_set_path_strv(Context *c, int rfd, char* const* strv, const char *source, const char *name, char ***dest) {
+        _cleanup_strv_free_ char **w = NULL;
+        int r;
+
+        assert(c);
+        assert(source);
+        assert(name);
+        assert(dest);
+
+        if (*dest)
+                return 0;
+
+        STRV_FOREACH(s, strv) {
+                char *p;
+
+                if (rfd >= 0)
+                        r = chaseat(rfd, *s, CHASE_AT_RESOLVE_IN_ROOT, &p, /* ret_fd = */ NULL);
+                else
+                        r = chase(*s, /* root = */ NULL, 0, &p, /* ret_fd = */ NULL);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to chase path %s for %s specified via %s: %m",
+                                                 *s, name, source);
+
+                r = strv_consume(&w, p);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        if (strv_isempty(w))
+                return 0;
+
+        log_debug("%s set via %s", name, source);
+
+        *dest = TAKE_PTR(w);
+        return 1;
+}
+
+static int context_set_plugins(Context *c, const char *s, const char *source) {
+        _cleanup_strv_free_ char **v = NULL;
+
+        assert(c);
+
+        if (c->plugins || !s)
+                return 0;
+
+        v = strv_split(s, NULL);
+        if (!v)
+                return log_oom();
+
+        return context_set_path_strv(c, c->rfd, v, source, "plugins", &c->plugins);
+}
+
+static int context_set_initrds(Context *c, char* const* strv) {
+        assert(c);
+        return context_set_path_strv(c, AT_FDCWD, strv, "command line", "initrds", &c->initrds);
+}
+
+static int context_load_environment(Context *c) {
+        assert(c);
+
+        (void) context_set_machine_id(c, getenv("MACHINE_ID"), "environment");
+        (void) context_set_boot_root(c, getenv("BOOT_ROOT"), "environment");
+        (void) context_set_conf_root(c, getenv("KERNEL_INSTALL_CONF_ROOT"), "environment");
+        (void) context_set_plugins(c, getenv("KERNEL_INSTALL_PLUGINS"), "environment");
+        return 0;
+}
+
+static int context_ensure_conf_root(Context *c) {
+        int r;
+
+        assert(c);
+
+        if (c->conf_root)
+                return 0;
+
+        r = chaseat(c->rfd, "/etc/kernel", CHASE_AT_RESOLVE_IN_ROOT, &c->conf_root, /* ret_fd = */ NULL);
+        if (r < 0)
+                log_debug_errno(r, "Failed to chase /etc/kernel, ignoring: %m");
+
+        return 0;
+}
+
+static int context_load_install_conf_one(Context *c, const char *path) {
+        _cleanup_close_ int fd = -EBADF;
+        _cleanup_free_ char
+                *conf = NULL, *machine_id = NULL, *boot_root = NULL, *layout = NULL,
+                *initrd_generator = NULL, *uki_generator = NULL;
+        int r;
+
+        assert(c);
+        assert(path);
+
+        conf = path_join(path, "install.conf");
+        if (!conf)
+                return log_oom();
+
+        r = chaseat(c->rfd, conf, CHASE_AT_RESOLVE_IN_ROOT, NULL, &fd);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to chase %s: %m", conf);
+
+        log_debug("Loading %s…", conf);
+
+        r = parse_env_file_fd(fd, conf,
+                              "MACHINE_ID",       &machine_id,
+                              "BOOT_ROOT",        &boot_root,
+                              "layout",           &layout,
+                              "initrd_generator", &initrd_generator,
+                              "uki_generator",    &uki_generator);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse '%s': %m", conf);
+
+        (void) context_set_machine_id(c, machine_id, conf);
+        (void) context_set_boot_root(c, boot_root, conf);
+        (void) context_set_layout(c, layout, conf);
+        (void) context_set_initrd_generator(c, initrd_generator, conf);
+        (void) context_set_uki_generator(c, uki_generator, conf);
+
+        log_debug("Loaded %s.", conf);
+        return 1;
+}
+
+static int context_load_install_conf(Context *c) {
+        int r;
+
+        assert(c);
+
+        if (c->conf_root) {
+                r = context_load_install_conf_one(c, c->conf_root);
+                if (r != 0)
+                        return r;
+        }
+
+        STRV_FOREACH(p, STRV_MAKE("/etc/kernel", "/usr/lib/kernel")) {
+                r = context_load_install_conf_one(c, *p);
+                if (r != 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int context_load_machine_info(Context *c) {
+        _cleanup_close_ int fd = -EBADF;
+        _cleanup_free_ char *machine_id = NULL;
+        static const char *path = "/etc/machine-info";
+        int r;
+
+        assert(c);
+
+        /* If the user configured an explicit machine ID in /etc/machine-info to use for our purpose, we'll
+         * use that instead (for compatibility). */
+
+        if (!sd_id128_is_null(c->machine_id))
+                return 0;
+
+        r = chaseat(c->rfd, path, CHASE_AT_RESOLVE_IN_ROOT, NULL, &fd);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to chase %s: %m", path);
+
+        log_debug("Loading %s…", path);
+
+        r = parse_env_file_fd(fd, path,
+                              "KERNEL_INSTALL_MACHINE_ID", &machine_id);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse '%s': %m", path);
+
+        (void) context_set_machine_id(c, machine_id, path);
+        return 0;
+}
+
+static int context_load_machine_id(Context *c) {
+        int r;
+
+        assert(c);
+
+        r = id128_get_machine_at(c->rfd, &c->machine_id);
+        if (r < 0) {
+                if (ERRNO_IS_MACHINE_ID_UNSET(r))
+                        return 0;
+                return log_error_errno(r, "Failed to load machine ID from /etc/machine-id: %m");
+        }
+
+        log_debug("MACHINE_ID=%s set via /etc/machine-id.", SD_ID128_TO_STRING(c->machine_id));
+        return 1; /* loaded */
+}
+
+static int context_ensure_machine_id(Context *c) {
+        int r;
+
+        assert(c);
+
+        if (!sd_id128_is_null(c->machine_id))
+                return 0;
+
+        /* If /etc/machine-id is initialized we'll use it. */
+        r = context_load_machine_id(c);
+        if (r != 0)
+                return r;
+
+        /* Otherwise we'll use a freshly generated one. */
+        r = sd_id128_randomize(&c->machine_id);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate random ID: %m");
+
+        c->machine_id_is_random = true;
+        log_debug("New machine ID '%s' generated.", SD_ID128_TO_STRING(c->machine_id));
+        return 0;
+}
+
+static int context_acquire_xbootldr(Context *c) {
+        int r;
+
+        assert(c);
+        assert(!c->boot_root);
+
+        r = find_xbootldr_and_warn_at(
+                        /* rfd = */ c->rfd,
+                        /* path = */ NULL,
+                        /* unprivileged_mode= */ -1,
+                        /* ret_path = */ &c->boot_root,
+                        /* ret_uuid = */ NULL,
+                        /* ret_devid = */ NULL);
+        if (r == -ENOKEY) {
+                log_debug_errno(r, "Couldn't find an XBOOTLDR partition.");
+                return 0;
+        }
+        if (r == -EACCES && geteuid() != 0)
+                return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
+        if (r < 0)
+                return r;
+
+        log_debug("Using XBOOTLDR partition at %s as $BOOT_ROOT.", c->boot_root);
+        return 1; /* found */
+}
+
+static int context_acquire_esp(Context *c) {
+        int r;
+
+        assert(c);
+        assert(!c->boot_root);
+
+        r = find_esp_and_warn_at(
+                        /* rfd = */ c->rfd,
+                        /* path = */ NULL,
+                        /* unprivileged_mode= */ -1,
+                        /* ret_path = */ &c->boot_root,
+                        /* ret_part = */ NULL,
+                        /* ret_pstart = */ NULL,
+                        /* ret_psize = */ NULL,
+                        /* ret_uuid = */ NULL,
+                        /* ret_devid = */ NULL);
+        if (r == -ENOKEY) {
+                log_debug_errno(r, "Couldn't find EFI system partition, ignoring.");
+                return 0;
+        }
+        if (r == -EACCES && geteuid() != 0)
+                return log_error_errno(r, "Failed to determine EFI system partition: %m");
+        if (r < 0)
+                return r;
+
+        log_debug("Using EFI System Partition at %s as $BOOT_ROOT.", c->boot_root);
+        return 1; /* found */
+}
+
+static int context_ensure_boot_root(Context *c) {
+        int r;
+
+        assert(c);
+
+        /* If BOOT_ROOT is specified via environment or install.conf, then use it. */
+        if (c->boot_root)
+                return 0;
+
+        /* Otherwise, use XBOOTLDR partition, if mounted. */
+        r = context_acquire_xbootldr(c);
+        if (r != 0)
+                return r;
+
+        /* Otherwise, use EFI system partition, if mounted. */
+        r = context_acquire_esp(c);
+        if (r != 0)
+                return r;
+
+        /* If all else fails, use /boot. */
+        r = chaseat(c->rfd, "/boot", CHASE_AT_RESOLVE_IN_ROOT, &c->boot_root, /* ret_fd = */ NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to chase '/boot': %m");
+
+        log_debug("KERNEL_INSTALL_BOOT_ROOT autodetection yielded no candidates, using \"%s\".", c->boot_root);
+        return 0;
+}
+
+static int context_ensure_entry_token(Context *c) {
+        int r;
+
+        assert(c);
+
+        /* Now that we determined the machine ID to use, let's determine the "token" for the boot loader
+         * entry to generate. We use that for naming the directory below $BOOT where we want to place the
+         * kernel/initrd and related resources, as well for naming the .conf boot loader spec entry.
+         * Typically this is just the machine ID, but it can be anything else, too, if we are told so. */
+
+        r = boot_entry_token_ensure_at(
+                        c->rfd,
+                        c->conf_root,
+                        c->machine_id,
+                        c->machine_id_is_random,
+                        &c->entry_token_type,
+                        &c->entry_token);
+        if (r < 0)
+                return r;
+
+        log_debug("Using entry token: %s", c->entry_token);
+        return 0;
+}
+
+static int context_load_plugins(Context *c) {
+        int r;
+
+        assert(c);
+
+        if (c->plugins)
+                return 0;
+
+        r = conf_files_list_strv_at(
+                        &c->plugins,
+                        ".install",
+                        c->rfd,
+                        CONF_FILES_EXECUTABLE | CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED,
+                        STRV_MAKE_CONST("/etc/kernel/install.d", "/usr/lib/kernel/install.d"));
+        if (r < 0)
+                return log_error_errno(r, "Failed to find plugins: %m");
+
+        return 0;
+}
+
+static int context_init(Context *c) {
+        int r;
+
+        assert(c);
+
+        r = context_open_root(c);
+        if (r < 0)
+                return r;
+
+        r = context_load_environment(c);
+        if (r < 0)
+                return r;
+
+        r = context_ensure_conf_root(c);
+        if (r < 0)
+                return r;
+
+        r = context_load_install_conf(c);
+        if (r < 0)
+                return r;
+
+        r = context_load_machine_info(c);
+        if (r < 0)
+                return r;
+
+        r = context_ensure_machine_id(c);
+        if (r < 0)
+                return r;
+
+        r = context_ensure_boot_root(c);
+        if (r < 0)
+                return r;
+
+        r = context_ensure_entry_token(c);
+        if (r < 0)
+                return r;
+
+        r = context_load_plugins(c);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int context_inspect_kernel(Context *c) {
+        assert(c);
+
+        if (!c->kernel)
+                return 0;
+
+        return inspect_kernel(c->rfd, c->kernel, &c->kernel_image_type, NULL, NULL, NULL);
+}
+
+static int context_ensure_layout(Context *c) {
+        int r;
+
+        assert(c);
+        assert(c->boot_root);
+        assert(c->entry_token);
+
+        if (c->layout >= 0 && c->layout != LAYOUT_AUTO)
+                return 0;
+
+        /* No layout configured by the administrator. Let's try to figure it out automatically from metadata
+         * already contained in $BOOT_ROOT. */
+
+        if (c->kernel_image_type == KERNEL_IMAGE_TYPE_UKI) {
+                c->layout = LAYOUT_UKI;
+                log_debug("Kernel image type is %s, using layout=%s.",
+                          kernel_image_type_to_string(c->kernel_image_type), layout_to_string(c->layout));
+                return 0;
+        }
+
+        _cleanup_free_ char *srel_path = path_join(c->boot_root, "loader/entries.srel");
+        if (!srel_path)
+                return log_oom();
+
+        _cleanup_free_ char *srel = NULL;
+        r = read_one_line_file_at(c->rfd, srel_path, &srel);
+        if (r >= 0) {
+                if (streq(srel, "type1"))
+                        /* The loader/entries.srel file clearly indicates that the installed boot loader
+                         * implements the proper standard upstream boot loader spec for Type #1 entries.
+                         * Let's default to that, then. */
+                        c->layout = LAYOUT_BLS;
+                else
+                        /* The loader/entries.srel file indicates some other spec is implemented and owns the
+                         * /loader/entries/ directory. Since we have no idea what that means, let's stay away
+                         * from it by default. */
+                        c->layout = LAYOUT_OTHER;
+
+                log_debug("%s with '%s' found, using layout=%s.", srel_path, srel, layout_to_string(c->layout));
+                return 0;
+        } else if (r != -ENOENT)
+                return log_error_errno(r, "Failed to read %s: %m", srel_path);
+
+        _cleanup_free_ char *entry_token_path = path_join(c->boot_root, c->entry_token);
+        if (!entry_token_path)
+                return log_oom();
+
+        r = is_dir_full(c->rfd, entry_token_path, /* follow = */ false);
+        if (r < 0 && r != -ENOENT)
+                return log_error_errno(r, "Failed to check if '%s' is a directory: %m", entry_token_path);
+        if (r > 0) {
+                /* If the metadata in $BOOT_ROOT doesn't tell us anything, then check if the entry token
+                 * directory already exists. If so, let's assume it's the standard boot loader spec, too. */
+                c->layout = LAYOUT_BLS;
+                log_debug("%s exists, using layout=%s.", entry_token_path, layout_to_string(c->layout));
+                return 0;
+        }
+
+        /* There's no metadata in $BOOT_ROOT, and apparently no entry token directory installed? Then we
+         * really don't know anything. */
+        c->layout = LAYOUT_OTHER;
+        log_debug("Entry-token directory not found, using layout=%s.", layout_to_string(c->layout));
+        return 0;
+}
+
+static int context_set_up_staging_area(Context *c) {
+        static const char *template = "/tmp/kernel-install.staging.XXXXXX";
+        int r;
+
+        assert(c);
+
+        if (c->staging_area)
+                return 0;
+
+        if (c->action == ACTION_INSPECT) {
+                /* This is only used for display. The directory will not be created. */
+                c->staging_area = strdup(template);
+                if (!c->staging_area)
+                        return log_oom();
+        } else {
+                r = mkdtemp_malloc(template, &c->staging_area);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create staging area: %m");
+        }
+
+        return 0;
+}
+
+static int context_build_entry_dir(Context *c) {
+        assert(c);
+        assert(c->boot_root);
+        assert(c->entry_token);
+        assert(c->version || c->action == ACTION_INSPECT);
+
+        if (c->entry_dir)
+                return 0;
+
+        c->entry_dir = path_join(c->boot_root, c->entry_token, c->version ?: "KERNEL_VERSION");
+        if (!c->entry_dir)
+                return log_oom();
+
+        log_debug("Using ENTRY_DIR=%s", c->entry_dir);
+        return 0;
+}
+
+static bool context_should_make_entry_dir(Context *c) {
+        assert(c);
+
+        /* Compatibility with earlier versions that used the presence of $BOOT_ROOT/$ENTRY_TOKEN to signal to
+         * 00-entry-directory to create $ENTRY_DIR to serve as the indication to use or to not use the BLS */
+
+        return c->layout == LAYOUT_BLS;
+}
+
+static int context_make_entry_dir(Context *c) {
+        _cleanup_close_ int fd = -EBADF;
+
+        assert(c);
+        assert(c->entry_dir);
+
+        if (c->action != ACTION_ADD)
+                return 0;
+
+        if (!context_should_make_entry_dir(c))
+                return 0;
+
+        log_debug("mkdir -p %s", c->entry_dir);
+        fd = chase_and_openat(c->rfd, c->entry_dir, CHASE_AT_RESOLVE_IN_ROOT | CHASE_MKDIR_0755,
+                              O_CLOEXEC | O_CREAT | O_DIRECTORY | O_PATH, NULL);
+        if (fd < 0)
+                return log_error_errno(fd, "Failed to make directory '%s': %m", c->entry_dir);
+
+        return 0;
+}
+
+static int context_remove_entry_dir(Context *c) {
+        _cleanup_free_ char *p = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        struct stat st;
+        int r;
+
+        assert(c);
+        assert(c->entry_dir);
+
+        if (c->action != ACTION_REMOVE)
+                return 0;
+
+        if (!context_should_make_entry_dir(c))
+                return 0;
+
+        log_debug("rm -rf %s", c->entry_dir);
+        fd = chase_and_openat(c->rfd, c->entry_dir, CHASE_AT_RESOLVE_IN_ROOT, O_CLOEXEC | O_DIRECTORY, &p);
+        if (fd < 0) {
+                if (IN_SET(fd, -ENOTDIR, -ENOENT))
+                        return 0;
+                return log_debug_errno(fd, "Failed to chase and open %s, ignoring: %m", c->entry_dir);
+        }
+
+        if (fstat(fd, &st) < 0)
+                return log_debug_errno(errno, "Failed to stat %s: %m", p);
+
+        r = rm_rf_children(TAKE_FD(fd), REMOVE_PHYSICAL|REMOVE_MISSING_OK|REMOVE_CHMOD, &st);
+        if (r < 0)
+                log_debug_errno(r, "Failed to remove children of %s, ignoring: %m", p);
+
+        if (unlinkat(c->rfd, p, AT_REMOVEDIR) < 0)
+                log_debug_errno(errno, "Failed to remove %s, ignoring: %m", p);
+
+        return 0;
+}
+
+static int context_build_arguments(Context *c) {
+        _cleanup_strv_free_ char **a = NULL;
+        const char *verb;
+        int r;
+
+        assert(c);
+        assert(c->entry_dir);
+
+        if (c->argv)
+                return 0;
+
+        switch (c->action) {
+        case ACTION_ADD:
+                assert(c->version);
+                assert(c->kernel);
+                verb = "add";
+                break;
+
+        case ACTION_REMOVE:
+                assert(c->version);
+                assert(!c->kernel);
+                assert(!c->initrds);
+                verb = "remove";
+                break;
+
+        case ACTION_INSPECT:
+                assert(!c->version);
+                assert(!c->initrds);
+                verb = "add|remove";
+                break;
+
+        default:
+                assert_not_reached();
+        }
+
+        a = strv_new("dummy-arg", /* to make strv_free() works for this variable. */
+                     verb,
+                     c->version ?: "KERNEL_VERSION",
+                     c->entry_dir);
+        if (!a)
+                return log_oom();
+
+        if (c->action == ACTION_ADD) {
+                r = strv_extend(&a, c->kernel);
+                if (r < 0)
+                        return log_oom();
+
+                r = strv_extend_strv(&a, c->initrds, /* filter_duplicates = */ false);
+                if (r < 0)
+                        return log_oom();
+
+        } else if (c->action == ACTION_INSPECT) {
+                r = strv_extend(&a, "[KERNEL_IMAGE]");
+                if (r < 0)
+                        return log_oom();
+
+                r = strv_extend(&a, "[INITRD...]");
+                if (r < 0)
+                        return log_oom();
+        }
+
+        c->argv = TAKE_PTR(a);
+        return 0;
+}
+
+static int context_build_environment(Context *c) {
+        _cleanup_strv_free_ char **e = NULL;
+        int r;
+
+        assert(c);
+
+        if (c->envp)
+                return 0;
+
+        r = strv_env_assign_many(&e,
+                                 "LC_COLLATE",                      SYSTEMD_DEFAULT_LOCALE,
+                                 "KERNEL_INSTALL_VERBOSE",          one_zero(arg_verbose),
+                                 "KERNEL_INSTALL_IMAGE_TYPE",       kernel_image_type_to_string(c->kernel_image_type),
+                                 "KERNEL_INSTALL_MACHINE_ID",       SD_ID128_TO_STRING(c->machine_id),
+                                 "KERNEL_INSTALL_ENTRY_TOKEN",      c->entry_token,
+                                 "KERNEL_INSTALL_BOOT_ROOT",        c->boot_root,
+                                 "KERNEL_INSTALL_LAYOUT",           context_get_layout(c),
+                                 "KERNEL_INSTALL_INITRD_GENERATOR", strempty(c->initrd_generator),
+                                 "KERNEL_INSTALL_UKI_GENERATOR",    strempty(c->uki_generator),
+                                 "KERNEL_INSTALL_STAGING_AREA",     c->staging_area);
+        if (r < 0)
+                return log_error_errno(r, "Failed to build environment variables for plugins: %m");
+
+        c->envp = TAKE_PTR(e);
+        return 0;
+}
+
+static int context_prepare_execution(Context *c) {
+        int r;
+
+        assert(c);
+
+        r = context_inspect_kernel(c);
+        if (r < 0)
+                return r;
+
+        r = context_ensure_layout(c);
+        if (r < 0)
+                return r;
+
+        r = context_set_up_staging_area(c);
+        if (r < 0)
+                return r;
+
+        r = context_build_entry_dir(c);
+        if (r < 0)
+                return r;
+
+        r = context_build_arguments(c);
+        if (r < 0)
+                return r;
+
+        r = context_build_environment(c);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int context_execute(Context *c) {
+        int r;
+
+        assert(c);
+
+        r = context_make_entry_dir(c);
+        if (r < 0)
+                return r;
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *x = strv_join_full(c->plugins, "", "\n  ", /* escape_separator = */ false);
+                log_debug("Using plugins: %s", strna(x));
+
+                _cleanup_free_ char *y = strv_join_full(c->envp, "", "\n  ", /* escape_separator = */ false);
+                log_debug("Plugin environment: %s", strna(y));
+
+                _cleanup_free_ char *z = strv_join(strv_skip(c->argv, 1), " ");
+                log_debug("Plugin arguments: %s", strna(z));
+        }
+
+        r = execute_strv(
+                        /* name = */ NULL,
+                        c->plugins,
+                        /* root = */ NULL,
+                        USEC_INFINITY,
+                        /* callbacks = */ NULL,
+                        /* callback_args = */ NULL,
+                        c->argv,
+                        c->envp,
+                        EXEC_DIR_SKIP_REMAINING);
+        if (r < 0)
+                return r;
+
+        r = context_remove_entry_dir(c);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int verb_add(int argc, char *argv[], void *userdata) {
+        Context *c = ASSERT_PTR(userdata);
+        int r;
+
+        assert(argc >= 3);
+        assert(argv);
+
+        c->action = ACTION_ADD;
+
+        r = context_set_version(c, argv[1]);
+        if (r < 0)
+                return r;
+
+        r = context_set_kernel(c, argv[2]);
+        if (r < 0)
+                return r;
+
+        r = context_set_initrds(c, strv_skip(argv, 3));
+        if (r < 0)
+                return r;
+
+        r = context_prepare_execution(c);
+        if (r < 0)
+                return r;
+
+        return context_execute(c);
+}
+
+static int run_as_installkernel(int argc, char *argv[], Context *c) {
+        /* kernel's install.sh invokes us as
+         *   /sbin/installkernel <version> <vmlinuz> <map> <installation-dir>
+         * We ignore the last two arguments. */
+        if (optind + 2 > argc)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'installkernel' command requires at least two arguments.");
+
+        return verb_add(3, STRV_MAKE("add", argv[optind], argv[optind+1]), c);
+}
+
+static int verb_remove(int argc, char *argv[], void *userdata) {
+        Context *c = ASSERT_PTR(userdata);
+        int r;
+
+        assert(argc == 2);
+        assert(argv);
+
+        c->action = ACTION_REMOVE;
+
+        r = context_set_version(c, argv[1]);
+        if (r < 0)
+                return r;
+
+        r = context_prepare_execution(c);
+        if (r < 0)
+                return r;
+
+        return context_execute(c);
+}
+
+static int verb_inspect(int argc, char *argv[], void *userdata) {
+        Context *c = ASSERT_PTR(userdata);
+        _cleanup_free_ char *joined = NULL;
+        int r;
+
+        c->action = ACTION_INSPECT;
+
+        r = context_prepare_execution(c);
+        if (r < 0)
+                return r;
+
+        printf("%sBoot Loader Entries:%s\n", ansi_underline(), ansi_normal());
+        printf("    $BOOT: %s\n", c->boot_root);
+        printf("    Token: %s\n", c->entry_token);
+        puts("");
+
+        printf("%sUsing plugins:%s\n", ansi_underline(), ansi_normal());
+        strv_print_full(c->plugins, "    ");
+        puts("");
+
+        printf("%sPlugin environment:%s\n", ansi_underline(), ansi_normal());
+        strv_print_full(c->envp, "    ");
+        puts("");
+
+        printf("%sPlugin arguments:%s\n", ansi_underline(), ansi_normal());
+        joined = strv_join(strv_skip(c->argv, 1), " ");
+        printf("    %s\n", strna(joined));
+
+        return 0;
+}
+
+static bool bypass(void) {
+        int r;
+
+        r = getenv_bool("KERNEL_INSTALL_BYPASS");
+        if (r < 0 && r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $KERNEL_INSTALL_BYPASS, assuming no.");
+        if (r <= 0)
+                return false;
+
+        log_debug("$KERNEL_INSTALL_BYPASS is enabled, skipping execution.");
+        return true;
+}
+
+static int help(void) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("kernel-install", "8", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s [OPTIONS...] COMMAND ...\n\n"
+               "%2$sAdd and remove kernel and initrd images to and from /boot%3$s\n"
+               "\nUsage:\n"
+               "  %1$s [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE...]\n"
+               "  %1$s [OPTIONS...] remove KERNEL-VERSION\n"
+               "  %1$s [OPTIONS...] inspect\n"
+               "\nOptions:\n"
+               "  -h --help              Show this help\n"
+               "     --version           Show package version\n"
+               "  -v --verbose           Increase verbosity\n"
+               "\nSee the %4$s for details.\n",
+               program_invocation_short_name,
+               ansi_highlight(),
+               ansi_normal(),
+               link);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        enum {
+                ARG_VERSION = 0x100,
+        };
+        static const struct option options[] = {
+                { "help",                 no_argument,       NULL, 'h'                      },
+                { "version",              no_argument,       NULL, ARG_VERSION              },
+                { "verbose",              no_argument,       NULL, 'v'                      },
+                {}
+        };
+        int t;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((t = getopt_long(argc, argv, "hv", options, NULL)) >= 0)
+                switch (t) {
+                case 'h':
+                        return help();
+
+                case ARG_VERSION:
+                        return version();
+
+                case 'v':
+                        log_set_max_level(LOG_DEBUG);
+                        arg_verbose = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+
+        return 1;
+}
+
+static int run(int argc, char* argv[]) {
+        static const Verb verbs[] = {
+                { "add",         3,        VERB_ANY, 0,            verb_add            },
+                { "remove",      2,        2,        0,            verb_remove         },
+                { "inspect",     1,        1,        VERB_DEFAULT, verb_inspect        },
+                {}
+        };
+        _cleanup_(context_done) Context c = {
+                .rfd = -EBADF,
+                .action = _ACTION_INVALID,
+                .kernel_image_type = KERNEL_IMAGE_TYPE_UNKNOWN,
+                .layout = _LAYOUT_INVALID,
+                .entry_token_type = BOOT_ENTRY_TOKEN_AUTO,
+        };
+        int r;
+
+        log_setup();
+
+        if (bypass())
+                return 0;
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        r = context_init(&c);
+        if (r < 0)
+                return r;
+
+        if (invoked_as(argv, "installkernel"))
+                return run_as_installkernel(argc, argv, &c);
+
+        return dispatch_verb(argc, argv, verbs, &c);
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/kernel-install/kernel-install.in b/src/kernel-install/kernel-install.in
deleted file mode 100755 (executable)
index 611f672..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-#!/bin/sh
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
-#
-# systemd is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with systemd; If not, see <https://www.gnu.org/licenses/>.
-
-skip_remaining=77
-
-set -e
-
-usage()
-{
-    echo "Usage:"
-    echo "  kernel-install [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE...]"
-    echo "  kernel-install [OPTIONS...] remove KERNEL-VERSION"
-    echo "  kernel-install [OPTIONS...] inspect"
-    echo "Options:"
-    echo "  -h, --help     Print this help and exit"
-    echo "      --version  Print version string and exit"
-    echo "  -v, --verbose  Increase verbosity"
-}
-
-dropindirs_sort()
-{
-    suffix="$1"
-    shift
-
-    for d; do
-        for i in "$d/"*"$suffix"; do
-            [ -e "$i" ] && echo "${i##*/}"
-        done
-    done | sort -Vu | while read -r f; do
-        for d; do
-            if [ -e "$d/$f" ]; then
-                [ -x "$d/$f" ] && echo "$d/$f"
-                continue 2
-            fi
-        done
-    done
-}
-
-export LC_COLLATE=C
-
-for i; do
-    if [ "$i" = "--help" ] || [ "$i" = "-h" ]; then
-        usage
-        exit 0
-    fi
-done
-
-for i; do
-    if [ "$i" = "--version" ]; then
-        echo "kernel-install {{PROJECT_VERSION}} ({{GIT_VERSION}})"
-        exit 0
-    fi
-done
-
-if [ "$KERNEL_INSTALL_BYPASS" = "1" ]; then
-    echo "kernel-install: Skipping execution because KERNEL_INSTALL_BYPASS=1"
-    exit 0
-fi
-
-export KERNEL_INSTALL_VERBOSE=0
-if [ "$1" = "--verbose" ] || [ "$1" = "-v" ]; then
-    shift
-    export KERNEL_INSTALL_VERBOSE=1
-    log_verbose() { printf "%s\n" "$*"; }
-else
-    log_verbose() { :; }
-fi
-
-if [ "${0##*/}" = "installkernel" ]; then
-    COMMAND=add
-    # kernel's install.sh invokes us as
-    #   /sbin/installkernel <version> <vmlinuz> <map> <installation-dir>
-    # We ignore the last two arguments.
-    set -- "${1:?}" "${2:?}"
-else
-    COMMAND="$1"
-    [ $# -ge 1 ] && shift
-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
-fi
-
-# These three settings are only settable via install.conf
-layout=
-initrd_generator=
-uki_generator=
-# These two settings can be inherited from the environment
-_MACHINE_ID_SAVED="$MACHINE_ID"
-_BOOT_ROOT_SAVED="$BOOT_ROOT"
-
-if [ -n "$KERNEL_INSTALL_CONF_ROOT" ]; then
-    install_conf="$KERNEL_INSTALL_CONF_ROOT/install.conf"
-elif [ -f "/etc/kernel/install.conf" ]; then
-    install_conf="/etc/kernel/install.conf"
-elif [ -f "/usr/lib/kernel/install.conf" ]; then
-    install_conf="/usr/lib/kernel/install.conf"
-else
-    install_conf=
-fi
-
-if [ -f "$install_conf" ]; then
-    log_verbose "Reading $install_conf…"
-    # shellcheck source=/dev/null
-    . "$install_conf"
-fi
-
-[ -n "$layout" ] && log_verbose "$install_conf configures layout=$layout"
-[ -n "$initrd_generator" ] && \
-    log_verbose "$install_conf configures initrd_generator=$initrd_generator"
-[ -n "$uki_generator" ] && \
-    log_verbose "$install_conf configures uki_generator=$uki_generator"
-
-if [ -n "$_MACHINE_ID_SAVED" ]; then
-    MACHINE_ID="$_MACHINE_ID_SAVED"
-    log_verbose "MACHINE_ID=$MACHINE_ID set via environment"
-else
-    [ -n "$MACHINE_ID" ] && log_verbose "MACHINE_ID=$MACHINE_ID set via install.conf"
-fi
-
-if [ -n "$_BOOT_ROOT_SAVED" ]; then
-     BOOT_ROOT="$_BOOT_ROOT_SAVED"
-     log_verbose "BOOT_ROOT=$BOOT_ROOT set via environment"
-else
-    [ -n "$BOOT_ROOT" ] && log_verbose "BOOT_ROOT=$BOOT_ROOT set via install.conf"
-fi
-
-# 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).
-# shellcheck source=/dev/null
-if [ -z "$MACHINE_ID" ] && [ -f /etc/machine-info ]; then
-    . /etc/machine-info
-    MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
-    [ -n "$MACHINE_ID" ] && \
-        log_verbose "machine-id $MACHINE_ID acquired from /etc/machine-info"
-fi
-if [ -z "$MACHINE_ID" ] && [ -s /etc/machine-id ]; then
-    read -r MACHINE_ID </etc/machine-id
-    [ "$MACHINE_ID" = "uninitialized" ] && unset MACHINE_ID
-    [ -n "$MACHINE_ID" ] && \
-        log_verbose "machine-id $MACHINE_ID acquired from /etc/machine-id"
-fi
-if [ -z "$MACHINE_ID" ]; then
-    MACHINE_ID="$(systemd-id128 new)" || exit 1
-    log_verbose "new machine-id $MACHINE_ID generated"
-fi
-
-# Now that we determined the machine ID to use, let's determine the "token" for
-# the boot loader entry to generate. We use that for naming the directory below
-# $BOOT where we want to place the kernel/initrd and related resources, as well
-# for naming the .conf boot loader spec entry. Typically this is just the
-# machine ID, but it can be anything else, too, if we are told so.
-ENTRY_TOKEN_FILE="${KERNEL_INSTALL_CONF_ROOT:-/etc/kernel}/entry-token"
-
-if [ -z "$ENTRY_TOKEN" ] && [ -f "$ENTRY_TOKEN_FILE" ]; then
-    read -r ENTRY_TOKEN <"$ENTRY_TOKEN_FILE"
-    log_verbose "entry-token \"$ENTRY_TOKEN\" acquired from $ENTRY_TOKEN_FILE"
-fi
-if [ -z "$ENTRY_TOKEN" ]; then
-    # If not configured explicitly, then use a few candidates: the machine ID,
-    # the IMAGE_ID= and ID= fields from /etc/os-release and finally the fixed
-    # string "Default"
-    ENTRY_TOKEN_SEARCH="$MACHINE_ID"
-    # shellcheck source=/dev/null
-    [ -f /etc/os-release ] && . /etc/os-release
-    [ -n "$IMAGE_ID" ] && ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH $IMAGE_ID"
-    [ -n "$ID" ] && ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH $ID"
-    ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH Default"
-else
-    ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN"
-fi
-log_verbose "Entry-token candidates: $ENTRY_TOKEN_SEARCH"
-
-# NB: The $MACHINE_ID is guaranteed to be a valid machine ID, but
-#     $ENTRY_TOKEN can be any string that fits into a VFAT filename, though
-#     typically is just the machine ID.
-
-if [ -n "$BOOT_ROOT" ]; then
-    # If this was already configured, don't try to guess
-    BOOT_ROOT_SEARCH="$BOOT_ROOT"
-else
-    BOOT_ROOT_SEARCH="/efi /boot /boot/efi"
-fi
-
-for pref in $BOOT_ROOT_SEARCH; do
-    for suff in $ENTRY_TOKEN_SEARCH; do
-        if [ -d "$pref/$suff" ]; then
-            [ -z "$BOOT_ROOT" ] && BOOT_ROOT="$pref"
-            [ -z "$ENTRY_TOKEN" ] && ENTRY_TOKEN="$suff"
-
-            log_verbose "$pref/$suff exists, using BOOT_ROOT=$BOOT_ROOT, ENTRY_TOKEN=$ENTRY_TOKEN"
-            break 2
-        else
-            log_verbose "$pref/$suff not found…"
-        fi
-    done
-
-    if [ -d "$pref/loader/entries" ]; then
-        [ -z "$BOOT_ROOT" ] && BOOT_ROOT="$pref"
-        log_verbose "$pref/loader/entries exists, using BOOT_ROOT=$BOOT_ROOT"
-        break
-    else
-        log_verbose "$pref/loader/entries not found…"
-    fi
-done
-
-[ -z "$BOOT_ROOT" ] && for pref in "/efi" "/boot/efi"; do
-    if mountpoint -q "$pref"; then
-        BOOT_ROOT="$pref"
-        log_verbose "$pref is a mount point, using BOOT_ROOT=$BOOT_ROOT"
-        break
-    else
-        log_verbose "$pref is not a mount point…"
-    fi
-done
-
-if [ -z "$BOOT_ROOT" ]; then
-    BOOT_ROOT="/boot"
-    log_verbose "KERNEL_INSTALL_BOOT_ROOT autodetection yielded no candidates, using \"$BOOT_ROOT\""
-fi
-
-if [ -z "$ENTRY_TOKEN" ]; then
-    ENTRY_TOKEN="$MACHINE_ID"
-    log_verbose "No entry-token candidate matched, using \"$ENTRY_TOKEN\" from machine-id"
-fi
-
-export KERNEL_INSTALL_IMAGE_TYPE=""
-if [ -f "$1" ]; then
-    KERNEL_INSTALL_IMAGE_TYPE="$(bootctl kernel-identify "$1" 2>/dev/null || echo "unknown")"
-fi
-
-if [ "$layout" = "auto" ] || [ -z "$layout" ]; then
-    # No layout configured by the administrator. Let's try to figure it out
-    # automatically from metadata already contained in $BOOT_ROOT.
-    if [ "$KERNEL_INSTALL_IMAGE_TYPE" = "uki" ]; then
-        layout="uki"
-        log_verbose "Kernel image is UKI, using layout=$layout"
-    elif [ -e "$BOOT_ROOT/loader/entries.srel" ]; then
-        read -r ENTRIES_SREL <"$BOOT_ROOT/loader/entries.srel"
-        if [ "$ENTRIES_SREL" = "type1" ]; then
-            # The loader/entries.srel file clearly indicates that the installed
-            # boot loader implements the proper standard upstream boot loader
-            # spec for Type #1 entries. Let's default to that, then.
-            layout="bls"
-        else
-            # The loader/entries.srel file indicates some other spec is
-            # implemented and owns the /loader/entries/ directory. Since we
-            # have no idea what that means, let's stay away from it by default.
-            layout="other"
-        fi
-        log_verbose "$BOOT_ROOT/loader/entries.srel with '$ENTRIES_SREL' found, using layout=$layout"
-
-    elif [ -d "$BOOT_ROOT/$ENTRY_TOKEN" ]; then
-        # If the metadata in $BOOT_ROOT doesn't tell us anything, then check if
-        # the entry token directory already exists. If so, let's assume it's
-        # the standard boot loader spec, too.
-        layout="bls"
-
-        log_verbose "$BOOT_ROOT/$ENTRY_TOKEN exists, using layout=$layout"
-    else
-        # There's no metadata in $BOOT_ROOT, and apparently no entry token
-        # directory installed? Then we really don't know anything.
-        layout="other"
-
-        log_verbose "Entry-token directory not found, using layout=$layout"
-    fi
-fi
-
-ENTRY_DIR_ABS="$BOOT_ROOT/$ENTRY_TOKEN/$KERNEL_VERSION"
-log_verbose "Using ENTRY_DIR_ABS=$ENTRY_DIR_ABS"
-
-# Provide a directory where to store generated initrds
-cleanup() {
-    [ -n "$KERNEL_INSTALL_STAGING_AREA" ] && rm -rf "$KERNEL_INSTALL_STAGING_AREA"
-}
-
-trap cleanup EXIT
-
-KERNEL_INSTALL_STAGING_AREA="$(mktemp -d -t kernel-install.staging.XXXXXXX)"
-
-export KERNEL_INSTALL_MACHINE_ID="$MACHINE_ID"
-export KERNEL_INSTALL_ENTRY_TOKEN="$ENTRY_TOKEN"
-export KERNEL_INSTALL_BOOT_ROOT="$BOOT_ROOT"
-export KERNEL_INSTALL_LAYOUT="$layout"
-export KERNEL_INSTALL_INITRD_GENERATOR="$initrd_generator"
-export KERNEL_INSTALL_UKI_GENERATOR="$uki_generator"
-export KERNEL_INSTALL_STAGING_AREA
-
-MAKE_ENTRY_DIR_ABS=0
-[ "$layout" = "bls" ] || MAKE_ENTRY_DIR_ABS=1
-
-ret=0
-
-if [ -z "$KERNEL_INSTALL_PLUGINS" ]; then
-    KERNEL_INSTALL_PLUGINS="$(
-        dropindirs_sort ".install" \
-            "/etc/kernel/install.d" \
-            "/usr/lib/kernel/install.d"
-    )"
-fi
-
-if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then
-    printf '%s\n' "Plugin files:"
-    for f in $KERNEL_INSTALL_PLUGINS; do
-        printf '%s\n' "$f"
-    done
-fi
-
-case "$COMMAND" in
-    add)
-        if [ $# -lt 1 ]; then
-            echo "Error: command 'add' requires a kernel image" >&2
-            exit 1
-        fi
-
-        if ! [ -f "$1" ]; then
-            echo "Error: kernel image argument $1 not a file" >&2
-            exit 1
-        fi
-
-        if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then
-            # Compatibility with earlier versions that used the presence of $BOOT_ROOT/$ENTRY_TOKEN
-            # to signal to 00-entry-directory to create $ENTRY_DIR_ABS
-            # to serve as the indication to use or to not use the BLS
-            if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then
-                echo "+mkdir -v -p $ENTRY_DIR_ABS"
-                mkdir -v -p "$ENTRY_DIR_ABS" || exit 1
-            else
-                mkdir -p "$ENTRY_DIR_ABS" || exit 1
-            fi
-        fi
-
-        for f in $KERNEL_INSTALL_PLUGINS; do
-            log_verbose "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS" "$@"
-            err=0
-            "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$@" || err=$?
-            [ $err -eq $skip_remaining ] && break
-            [ $err -ne 0 ] && exit $err
-        done
-        ;;
-
-    remove)
-        for f in $KERNEL_INSTALL_PLUGINS; do
-            log_verbose "+$f remove $KERNEL_VERSION $ENTRY_DIR_ABS"
-            err=0
-            "$f" remove "$KERNEL_VERSION" "$ENTRY_DIR_ABS" || err=$?
-            [ $err -eq $skip_remaining ] && break
-            [ $err -ne 0 ] && exit $err
-        done
-
-        if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then
-            log_verbose "Removing $ENTRY_DIR_ABS/"
-            rm -rf "$ENTRY_DIR_ABS"
-        fi
-        ;;
-
-    inspect)
-        echo "KERNEL_INSTALL_MACHINE_ID: $KERNEL_INSTALL_MACHINE_ID"
-        echo "KERNEL_INSTALL_ENTRY_TOKEN: $KERNEL_INSTALL_ENTRY_TOKEN"
-        echo "KERNEL_INSTALL_BOOT_ROOT: $KERNEL_INSTALL_BOOT_ROOT"
-        echo "KERNEL_INSTALL_LAYOUT: $KERNEL_INSTALL_LAYOUT"
-        echo "KERNEL_INSTALL_INITRD_GENERATOR: $KERNEL_INSTALL_INITRD_GENERATOR"
-        echo "KERNEL_INSTALL_UKI_GENERATOR: $KERNEL_INSTALL_UKI_GENERATOR"
-        echo "ENTRY_DIR_ABS: $KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN/\$KERNEL_VERSION"
-
-        # Assert that ENTRY_DIR_ABS actually matches what we are printing here
-        [ "${ENTRY_DIR_ABS%/*}" = "$KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN" ] || { echo "Assertion didn't pass." >&2; exit 1; }
-        ;;
-
-    *)
-        echo "Error: unknown command '$COMMAND'" >&2
-        exit 1
-        ;;
-esac
-
-exit "$ret"
index 744071b9e94c021dc28f96ca6e63198ce0620ac9..6d568966d61f0fcbfa1501733a8618a810261d60 100644 (file)
@@ -1,7 +1,5 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-kernel_install_in = files('kernel-install.in')
-
 ukify_install = custom_target(
         '60-ukify.install',
         input : '60-ukify.install.in',