summaryrefslogtreecommitdiffstats
path: root/src/kernel-install/kernel-install.in
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel-install/kernel-install.in')
-rwxr-xr-xsrc/kernel-install/kernel-install.in393
1 files changed, 393 insertions, 0 deletions
diff --git a/src/kernel-install/kernel-install.in b/src/kernel-install/kernel-install.in
new file mode 100755
index 0000000..fa2c0d5
--- /dev/null
+++ b/src/kernel-install/kernel-install.in
@@ -0,0 +1,393 @@
+#!/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 two settings are only settable via install.conf
+layout=
+initrd_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"
+
+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
+ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "$pref/$suff not found…"
+ fi
+
+ if [ -d "$pref/loader/entries" ]; then
+ [ -z "$BOOT_ROOT" ] && BOOT_ROOT="$pref"
+ log_verbose "$pref/loader/entries exists, using BOOT_ROOT=$BOOT_ROOT"
+ break 2
+ else
+ log_verbose "$pref/loader/entries not found…"
+ fi
+ done
+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"
+ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
+ echo "KERNEL_INSTALL_BOOT_ROOT autodection yielded no candidates, using \"$BOOT_ROOT\""
+fi
+
+if [ -z "$ENTRY_TOKEN" ]; then
+ ENTRY_TOKEN="$MACHINE_ID"
+ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
+ echo "No entry-token candidate matched, using \"$ENTRY_TOKEN\" from machine-id"
+fi
+
+if [ -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 [ -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
+ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
+ echo "$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_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 "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"