1#!/bin/sh 2# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- 3# ex: ts=8 sw=4 sts=4 et filetype=sh 4# SPDX-License-Identifier: LGPL-2.1-or-later 5# 6# This file is part of systemd. 7# 8# systemd is free software; you can redistribute it and/or modify it 9# under the terms of the GNU Lesser General Public License as published by 10# the Free Software Foundation; either version 2.1 of the License, or 11# (at your option) any later version. 12# 13# systemd is distributed in the hope that it will be useful, but 14# WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16# General Public License for more details. 17# 18# You should have received a copy of the GNU Lesser General Public License 19# along with systemd; If not, see <http://www.gnu.org/licenses/>. 20 21skip_remaining=77 22 23usage() 24{ 25 echo "Usage:" 26 echo " kernel-install [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE...]" 27 echo " kernel-install [OPTIONS...] remove KERNEL-VERSION" 28 echo " kernel-install [OPTIONS...] inspect" 29 echo "Options:" 30 echo " -h, --help Print this help and exit" 31 echo " --version Print version string and exit" 32 echo " -v, --verbose Increase verbosity" 33} 34 35dropindirs_sort() 36{ 37 suffix="$1" 38 shift 39 40 for d; do 41 for i in "$d/"*"$suffix"; do 42 [ -e "$i" ] && echo "${i##*/}" 43 done 44 done | sort -Vu | while read -r f; do 45 for d; do 46 if [ -e "$d/$f" ]; then 47 [ -x "$d/$f" ] && echo "$d/$f" 48 continue 2 49 fi 50 done 51 done 52} 53 54export LC_COLLATE=C 55 56for i; do 57 if [ "$i" = "--help" ] || [ "$i" = "-h" ]; then 58 usage 59 exit 0 60 fi 61done 62 63for i; do 64 if [ "$i" = "--version" ]; then 65 echo "kernel-install {{PROJECT_VERSION}} ({{GIT_VERSION}})" 66 exit 0 67 fi 68done 69 70if [ "$KERNEL_INSTALL_BYPASS" = "1" ]; then 71 echo "kernel-install: Skipping execution because KERNEL_INSTALL_BYPASS=1" 72 exit 0 73fi 74 75export KERNEL_INSTALL_VERBOSE=0 76if [ "$1" = "--verbose" ] || [ "$1" = "-v" ]; then 77 shift 78 export KERNEL_INSTALL_VERBOSE=1 79fi 80 81if [ "${0##*/}" = "installkernel" ]; then 82 COMMAND=add 83 # make install doesn't pass any initrds 84else 85 COMMAND="$1" 86 [ $# -ge 1 ] && shift 87fi 88 89if [ "$COMMAND" = "inspect" ]; then 90 KERNEL_VERSION="" 91else 92 if [ $# -lt 1 ]; then 93 echo "Error: not enough arguments" >&2 94 exit 1 95 fi 96 97 KERNEL_VERSION="$1" 98 shift 99fi 100 101# These two settings are settable in install.conf 102layout= 103initrd_generator= 104 105if [ -r "/etc/kernel/install.conf" ]; then 106 install_conf="/etc/kernel/install.conf" 107elif [ -r "/usr/lib/kernel/install.conf" ]; then 108 install_conf="/usr/lib/kernel/install.conf" 109else 110 install_conf= 111fi 112 113if [ -n "$install_conf" ]; then 114 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Reading $install_conf…" 115 . "$install_conf" 116 # FIXME: This may override configuration in environment variables, e.g. $BOOT_ROOT. 117fi 118 119[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && [ -n "$layout" ] && \ 120 echo "$install_conf configures layout=$layout" 121[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && [ -n "$initrd_generator" ] && \ 122 echo "$install_conf configures initrd_generator=$initrd_generator" 123 124[ -n "$MACHINE_ID" ] && [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 125 echo "machine-id $MACHINE_ID set via environment or install.conf" 126[ -n "$BOOT_ROOT" ] && [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 127 echo "BOOT_ROOT=$BOOT_ROOT set via environment or install.conf" 128 129# If /etc/machine-id is initialized we'll use it, otherwise we'll use a freshly 130# generated one. If the user configured an explicit machine ID to use in 131# /etc/machine-info to use for our purpose, we'll use that instead (for 132# compatibility). 133if [ -z "$MACHINE_ID" ] && [ -r /etc/machine-info ] && . /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"; then 134 [ -n "$MACHINE_ID" ] && [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 135 echo "machine-id $MACHINE_ID acquired from /etc/machine-info" 136fi 137if [ -z "$MACHINE_ID" ] && [ -r /etc/machine-id ] && read -r MACHINE_ID </etc/machine-id; then 138 [ -n "$MACHINE_ID" ] && [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 139 echo "machine-id $MACHINE_ID acquired from /etc/machine-id" 140fi 141if [ -z "$MACHINE_ID" ]; then 142 MACHINE_ID="$(systemd-id128 new)" || exit 1 143 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "new machine-id $MACHINE_ID generated" 144fi 145 146# Now that we determined the machine ID to use, let's determine the "token" for 147# the boot loader entry to generate. We use that for naming the directory below 148# $BOOT where we want to place the kernel/initrd and related resources, as well 149# for naming the .conf boot loader spec entry. Typically this is just the 150# machine ID, but it can be anything else, too, if we are told so. 151if [ -z "$ENTRY_TOKEN" ] && [ -r /etc/kernel/entry-token ] && read -r ENTRY_TOKEN </etc/kernel/entry-token; then 152 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 153 echo "entry-token \"$ENTRY_TOKEN\" acquired from /etc/kernel/entry-token" 154fi 155if [ -z "$ENTRY_TOKEN" ]; then 156 # If not configured explicitly, then use a few candidates: the machine ID, 157 # the IMAGE_ID= and ID= fields from /etc/os-release and finally the fixed 158 # string "Default" 159 ENTRY_TOKEN_SEARCH="$MACHINE_ID" 160 [ -r /etc/os-release ] && . /etc/os-release 161 [ -n "$IMAGE_ID" ] && ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH $IMAGE_ID" 162 [ -n "$ID" ] && ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH $ID" 163 ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH Default" 164else 165 ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN" 166fi 167[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Entry-token candidates: $ENTRY_TOKEN_SEARCH" 168 169# NB: The $MACHINE_ID is guaranteed to be a valid machine ID, but 170# $ENTRY_TOKEN can be any string that fits into a VFAT filename, though 171# typically is just the machine ID. 172 173if [ -n "$BOOT_ROOT" ]; then 174 # If this was already configured, don't try to guess 175 BOOT_ROOT_SEARCH="$BOOT_ROOT" 176else 177 BOOT_ROOT_SEARCH="/efi /boot /boot/efi" 178fi 179 180for pref in $BOOT_ROOT_SEARCH; do 181 for suff in $ENTRY_TOKEN_SEARCH; do 182 if [ -d "$pref/$suff" ]; then 183 [ -z "$BOOT_ROOT" ] && BOOT_ROOT="$pref" 184 [ -z "$ENTRY_TOKEN" ] && ENTRY_TOKEN="$suff" 185 186 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 187 echo "$pref/$suff exists, using BOOT_ROOT=$BOOT_ROOT, ENTRY_TOKEN=$ENTRY_TOKEN" 188 break 2 189 else 190 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "$pref/$suff not found…" 191 fi 192 193 if [ -d "$pref/loader/entries" ]; then 194 [ -z "$BOOT_ROOT" ] && BOOT_ROOT="$pref" 195 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 196 echo "$pref/loader/entries exists, using BOOT_ROOT=$BOOT_ROOT" 197 break 2 198 else 199 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "$pref/loader/entries not found…" 200 fi 201 done 202done 203 204[ -z "$BOOT_ROOT" ] && for pref in "/efi" "/boot/efi"; do 205 if mountpoint -q "$pref"; then 206 BOOT_ROOT="$pref" 207 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 208 echo "$pref is a mount point, using BOOT_ROOT=$BOOT_ROOT" 209 break 210 else 211 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "$pref is not a mount point…" 212 fi 213done 214 215if [ -z "$BOOT_ROOT" ]; then 216 BOOT_ROOT="/boot" 217 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 218 echo "KERNEL_INSTALL_BOOT_ROOT autodection yielded no candidates, using \"$BOOT_ROOT\"" 219fi 220 221if [ -z "$ENTRY_TOKEN" ]; then 222 ENTRY_TOKEN="$MACHINE_ID" 223 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 224 echo "No entry-token candidate matched, using \"$ENTRY_TOKEN\" from machine-id" 225fi 226 227if [ -z "$layout" ]; then 228 # No layout configured by the administrator. Let's try to figure it out 229 # automatically from metadata already contained in $BOOT_ROOT. 230 if [ -e "$BOOT_ROOT/loader/entries.srel" ]; then 231 read -r ENTRIES_SREL <"$BOOT_ROOT/loader/entries.srel" 232 if [ "$ENTRIES_SREL" = "type1" ]; then 233 # The loader/entries.srel file clearly indicates that the installed 234 # boot loader implements the proper standard upstream boot loader 235 # spec for Type #1 entries. Let's default to that, then. 236 layout="bls" 237 else 238 # The loader/entries.srel file indicates some other spec is 239 # implemented and owns the /loader/entries/ directory. Since we 240 # have no idea what that means, let's stay away from it by default. 241 layout="other" 242 fi 243 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ 244 echo "$BOOT_ROOT/loader/entries.srel with '$ENTRIES_SREL' found, using layout=$layout" 245 246 elif [ -d "$BOOT_ROOT/$ENTRY_TOKEN" ]; then 247 # If the metadata in $BOOT_ROOT doesn't tell us anything, then check if 248 # the entry token directory already exists. If so, let's assume it's 249 # the standard boot loader spec, too. 250 layout="bls" 251 252 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "$BOOT_ROOT/$ENTRY_TOKEN exists, using layout=$layout" 253 else 254 # There's no metadata in $BOOT_ROOT, and apparently no entry token 255 # directory installed? Then we really don't know anything. 256 layout="other" 257 258 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Entry-token directory not found, using layout=$layout" 259 fi 260fi 261 262ENTRY_DIR_ABS="$BOOT_ROOT/$ENTRY_TOKEN/$KERNEL_VERSION" 263[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Using ENTRY_DIR_ABS=$ENTRY_DIR_ABS" 264 265# Provide a directory where to store generated initrds 266cleanup() { 267 [ -n "$KERNEL_INSTALL_STAGING_AREA" ] && rm -rf "$KERNEL_INSTALL_STAGING_AREA" 268} 269 270trap cleanup EXIT 271 272KERNEL_INSTALL_STAGING_AREA="$(mktemp -d -t kernel-install.staging.XXXXXXX)" 273 274export KERNEL_INSTALL_MACHINE_ID="$MACHINE_ID" 275export KERNEL_INSTALL_ENTRY_TOKEN="$ENTRY_TOKEN" 276export KERNEL_INSTALL_BOOT_ROOT="$BOOT_ROOT" 277export KERNEL_INSTALL_LAYOUT="$layout" 278export KERNEL_INSTALL_INITRD_GENERATOR="$initrd_generator" 279export KERNEL_INSTALL_STAGING_AREA 280 281[ "$layout" = "bls" ] 282MAKE_ENTRY_DIR_ABS=$? 283 284ret=0 285 286PLUGINS="$( 287 dropindirs_sort ".install" \ 288 "/etc/kernel/install.d" \ 289 "/usr/lib/kernel/install.d" 290)" 291IFS=" 292" 293 294[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo -e "Plugin files:\n$PLUGINS" 295 296case "$COMMAND" in 297 add) 298 if [ $# -lt 1 ]; then 299 echo "Error: command 'add' requires a kernel image" >&2 300 exit 1 301 fi 302 303 if ! [ -f "$1" ]; then 304 echo "Error: kernel image argument $1 not a file" >&2 305 exit 1 306 fi 307 308 if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then 309 # Compatibility with earlier versions that used the presence of $BOOT_ROOT/$ENTRY_TOKEN 310 # to signal to 00-entry-directory to create $ENTRY_DIR_ABS 311 # to serve as the indication to use or to not use the BLS 312 if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then 313 echo "+mkdir -v -p $ENTRY_DIR_ABS" 314 mkdir -v -p "$ENTRY_DIR_ABS" || exit 1 315 else 316 mkdir -p "$ENTRY_DIR_ABS" || exit 1 317 fi 318 fi 319 320 for f in $PLUGINS; do 321 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS $*" 322 "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$@" 323 err=$? 324 [ $err -eq $skip_remaining ] && break 325 ret=$(( ret + err )) 326 done 327 ;; 328 329 remove) 330 for f in $PLUGINS; do 331 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f remove $KERNEL_VERSION $ENTRY_DIR_ABS" 332 "$f" remove "$KERNEL_VERSION" "$ENTRY_DIR_ABS" 333 err=$? 334 [ $err -eq $skip_remaining ] && break 335 ret=$(( ret + err )) 336 done 337 338 if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then 339 [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Removing $ENTRY_DIR_ABS/" 340 rm -rf "$ENTRY_DIR_ABS" 341 fi 342 ;; 343 344 inspect) 345 echo "KERNEL_INSTALL_MACHINE_ID: $KERNEL_INSTALL_MACHINE_ID" 346 echo "KERNEL_INSTALL_ENTRY_TOKEN: $KERNEL_INSTALL_ENTRY_TOKEN" 347 echo "KERNEL_INSTALL_BOOT_ROOT: $KERNEL_INSTALL_BOOT_ROOT" 348 echo "KERNEL_INSTALL_LAYOUT: $KERNEL_INSTALL_LAYOUT" 349 echo "KERNEL_INSTALL_INITRD_GENERATOR: $KERNEL_INSTALL_INITRD_GENERATOR" 350 echo "ENTRY_DIR_ABS: $KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN/\$KERNEL_VERSION" 351 352 # Assert that ENTRY_DIR_ABS actually matches what we are printing here 353 [ "${ENTRY_DIR_ABS%/*}" = "$KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN" ] || { echo "Assertion didn't pass." >&2; exit 1; } 354 355 ;; 356 *) 357 echo "Error: unknown command '$COMMAND'" >&2 358 exit 1 359 ;; 360esac 361 362exit "$ret" 363