diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:54:25 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:54:25 +0000 |
commit | 9cb1c4df7b9ce1a9ad1312621b0f2b16a94fba3a (patch) | |
tree | 2efb72864cc69e174c9c5ee33efb88a5f1553b48 /modules.d | |
parent | Initial commit. (diff) | |
download | dracut-9cb1c4df7b9ce1a9ad1312621b0f2b16a94fba3a.tar.xz dracut-9cb1c4df7b9ce1a9ad1312621b0f2b16a94fba3a.zip |
Adding upstream version 060+5.upstream/060+5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
379 files changed, 23187 insertions, 0 deletions
diff --git a/modules.d/00bash/module-setup.sh b/modules.d/00bash/module-setup.sh new file mode 100755 index 0000000..89ef654 --- /dev/null +++ b/modules.d/00bash/module-setup.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries bash || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst /bin/bash + + # Prefer bash as default shell if no other shell is preferred. + [[ -L $initdir/bin/sh ]] || ln -sf bash "${initdir}/bin/sh" + +} diff --git a/modules.d/00dash/module-setup.sh b/modules.d/00dash/module-setup.sh new file mode 100755 index 0000000..4a0c0e7 --- /dev/null +++ b/modules.d/00dash/module-setup.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries dash || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst /bin/dash + + # Prefer dash as default shell if no other shell is preferred. + [[ -L $initdir/bin/sh ]] || ln -sf dash "${initdir}/bin/sh" + +} diff --git a/modules.d/00mksh/module-setup.sh b/modules.d/00mksh/module-setup.sh new file mode 100755 index 0000000..8942b42 --- /dev/null +++ b/modules.d/00mksh/module-setup.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries mksh || return 1 + require_binaries printf || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst /bin/mksh + inst printf + + # Prefer mksh as default shell if no other shell is preferred. + [[ -L $initdir/bin/sh ]] || ln -sf mksh "${initdir}/bin/sh" + +} diff --git a/modules.d/00systemd-network-management/module-setup.sh b/modules.d/00systemd-network-management/module-setup.sh new file mode 100755 index 0000000..f8ba351 --- /dev/null +++ b/modules.d/00systemd-network-management/module-setup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd systemd-hostnamed systemd-networkd systemd-resolved systemd-timedated systemd-timesyncd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} diff --git a/modules.d/00systemd/module-setup.sh b/modules.d/00systemd/module-setup.sh new file mode 100755 index 0000000..9a13a1d --- /dev/null +++ b/modules.d/00systemd/module-setup.sh @@ -0,0 +1,267 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + [[ $mount_needs ]] && return 1 + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries "$systemdutildir"/systemd || return 1 + # Return 255 to only include the module, if another module requires it. + return 255 +} + +# called by dracut +depends() { + return 0 +} + +installkernel() { + hostonly='' instmods autofs4 ipv6 algif_hash hmac sha256 + instmods -s efivarfs +} + +# called by dracut +install() { + local _mods + + if [[ $prefix == /run/* ]]; then + dfatal 'systemd does not work with a prefix, which contains "/run"!!' + exit 1 + fi + + inst_multiple -o \ + "$systemdutildir"/systemd \ + "$systemdutildir"/systemd-coredump \ + "$systemdutildir"/systemd-cgroups-agent \ + "$systemdutildir"/systemd-executor \ + "$systemdutildir"/systemd-shutdown \ + "$systemdutildir"/systemd-reply-password \ + "$systemdutildir"/systemd-fsck \ + "$systemdutildir"/systemd-udevd \ + "$systemdutildir"/systemd-journald \ + "$systemdutildir"/systemd-sysctl \ + "$systemdutildir"/systemd-modules-load \ + "$systemdutildir"/systemd-vconsole-setup \ + "$systemdutildir"/systemd-volatile-root \ + "$systemdutildir"/systemd-sysroot-fstab-check \ + "$systemdutildir"/system-generators/systemd-debug-generator \ + "$systemdutildir"/system-generators/systemd-fstab-generator \ + "$systemdutildir"/system-generators/systemd-gpt-auto-generator \ + "$systemdsystemunitdir"/debug-shell.service \ + "$systemdsystemunitdir"/cryptsetup.target \ + "$systemdsystemunitdir"/cryptsetup-pre.target \ + "$systemdsystemunitdir"/remote-cryptsetup.target \ + "$systemdsystemunitdir"/emergency.target \ + "$systemdsystemunitdir"/sysinit.target \ + "$systemdsystemunitdir"/basic.target \ + "$systemdsystemunitdir"/halt.target \ + "$systemdsystemunitdir"/kexec.target \ + "$systemdsystemunitdir"/local-fs.target \ + "$systemdsystemunitdir"/local-fs-pre.target \ + "$systemdsystemunitdir"/remote-fs.target \ + "$systemdsystemunitdir"/remote-fs-pre.target \ + "$systemdsystemunitdir"/multi-user.target \ + "$systemdsystemunitdir"/network.target \ + "$systemdsystemunitdir"/network-pre.target \ + "$systemdsystemunitdir"/network-online.target \ + "$systemdsystemunitdir"/nss-lookup.target \ + "$systemdsystemunitdir"/nss-user-lookup.target \ + "$systemdsystemunitdir"/poweroff.target \ + "$systemdsystemunitdir"/reboot.target \ + "$systemdsystemunitdir"/rescue.target \ + "$systemdsystemunitdir"/rpcbind.target \ + "$systemdsystemunitdir"/shutdown.target \ + "$systemdsystemunitdir"/final.target \ + "$systemdsystemunitdir"/sigpwr.target \ + "$systemdsystemunitdir"/sockets.target \ + "$systemdsystemunitdir"/swap.target \ + "$systemdsystemunitdir"/timers.target \ + "$systemdsystemunitdir"/paths.target \ + "$systemdsystemunitdir"/umount.target \ + "$systemdsystemunitdir"/sys-kernel-config.mount \ + "$systemdsystemunitdir"/modprobe@.service \ + "$systemdsystemunitdir"/kmod-static-nodes.service \ + "$systemdsystemunitdir"/systemd-tmpfiles-setup.service \ + "$systemdsystemunitdir"/systemd-tmpfiles-setup-dev.service \ + "$systemdsystemunitdir"/systemd-tmpfiles-setup-dev-early.service \ + "$systemdsystemunitdir"/systemd-ask-password-console.path \ + "$systemdsystemunitdir"/systemd-udevd-control.socket \ + "$systemdsystemunitdir"/systemd-udevd-kernel.socket \ + "$systemdsystemunitdir"/systemd-ask-password-plymouth.path \ + "$systemdsystemunitdir"/systemd-journald.socket \ + "$systemdsystemunitdir"/systemd-journald-audit.socket \ + "$systemdsystemunitdir"/systemd-ask-password-console.service \ + "$systemdsystemunitdir"/systemd-modules-load.service \ + "$systemdsystemunitdir"/systemd-halt.service \ + "$systemdsystemunitdir"/systemd-poweroff.service \ + "$systemdsystemunitdir"/systemd-reboot.service \ + "$systemdsystemunitdir"/systemd-kexec.service \ + "$systemdsystemunitdir"/systemd-fsck@.service \ + "$systemdsystemunitdir"/systemd-udevd.service \ + "$systemdsystemunitdir"/systemd-udev-trigger.service \ + "$systemdsystemunitdir"/systemd-udev-settle.service \ + "$systemdsystemunitdir"/systemd-ask-password-plymouth.service \ + "$systemdsystemunitdir"/systemd-journald.service \ + "$systemdsystemunitdir"/systemd-vconsole-setup.service \ + "$systemdsystemunitdir"/systemd-volatile-root.service \ + "$systemdsystemunitdir"/systemd-sysctl.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-modules-load.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-ask-password-console.path \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-journald.service \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-udevd-control.socket \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-udevd-kernel.socket \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-journald.socket \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-journald-audit.socket \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-journald-dev-log.socket \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-udevd.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-udev-trigger.service \ + "$systemdsystemunitdir"/sysinit.target.wants/kmod-static-nodes.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-tmpfiles-setup.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-tmpfiles-setup-dev.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-tmpfiles-setup-dev-early.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-sysctl.service \ + "$systemdsystemunitdir"/ctrl-alt-del.target \ + "$systemdsystemunitdir"/reboot.target \ + "$systemdsystemunitdir"/systemd-reboot.service \ + "$systemdsystemunitdir"/syslog.socket \ + "$systemdsystemunitdir"/slices.target \ + "$systemdsystemunitdir"/system.slice \ + "$systemdsystemunitdir"/-.slice \ + "$tmpfilesdir"/systemd.conf \ + journalctl systemctl \ + echo swapoff \ + kmod insmod rmmod modprobe modinfo depmod lsmod \ + mount umount reboot poweroff \ + systemd-run systemd-escape \ + systemd-cgls systemd-tmpfiles \ + systemd-ask-password systemd-tty-ask-password-agent \ + /etc/udev/udev.hwdb + + inst_multiple -o \ + /usr/lib/modules-load.d/*.conf \ + /usr/lib/sysctl.d/*.conf + + modules_load_get() { + local _line i + for i in "$dracutsysrootdir$1"/*.conf; do + [[ -f $i ]] || continue + while read -r _line || [ -n "$_line" ]; do + case $_line in + \#*) ;; + + \;*) ;; + + *) + echo "$_line" + ;; + esac + done < "$i" + done + } + + mapfile -t _mods < <(modules_load_get /usr/lib/modules-load.d) + [[ ${#_mods[@]} -gt 0 ]] && hostonly='' instmods "${_mods[@]}" + + if [[ $hostonly ]]; then + inst_multiple -H -o \ + /etc/systemd/journald.conf \ + /etc/systemd/journald.conf.d/*.conf \ + /etc/systemd/system.conf \ + /etc/systemd/system.conf.d/*.conf \ + "$systemdsystemconfdir"/modprobe@.service \ + "$systemdsystemconfdir/modprobe@.service.d/*.conf" \ + /etc/hosts \ + /etc/hostname \ + /etc/nsswitch.conf \ + /etc/machine-id \ + /etc/machine-info \ + /etc/vconsole.conf \ + /etc/locale.conf \ + /etc/modules-load.d/*.conf \ + /etc/sysctl.d/*.conf \ + /etc/sysctl.conf \ + /etc/udev/udev.conf + + mapfile -t _mods < <(modules_load_get /etc/modules-load.d) + [[ ${#_mods[@]} -gt 0 ]] && hostonly='' instmods "${_mods[@]}" + fi + + if ! [[ -e "$initdir/etc/machine-id" ]]; then + : > "$initdir/etc/machine-id" + chmod 444 "$initdir/etc/machine-id" + fi + + # install adm user/group for journald + inst_multiple nologin + { + grep '^systemd-journal:' "$dracutsysrootdir"/etc/passwd 2> /dev/null + grep '^adm:' "$dracutsysrootdir"/etc/passwd 2> /dev/null + # we don't use systemd-networkd, but the user is in systemd.conf tmpfiles snippet + grep '^systemd-network:' "$dracutsysrootdir"/etc/passwd 2> /dev/null + } >> "$initdir/etc/passwd" + + { + grep '^systemd-journal:' "$dracutsysrootdir"/etc/group 2> /dev/null + grep '^wheel:' "$dracutsysrootdir"/etc/group 2> /dev/null + grep '^adm:' "$dracutsysrootdir"/etc/group 2> /dev/null + grep '^utmp:' "$dracutsysrootdir"/etc/group 2> /dev/null + grep '^root:' "$dracutsysrootdir"/etc/group 2> /dev/null + # we don't use systemd-networkd, but the user is in systemd.conf tmpfiles snippet + grep '^systemd-network:' "$dracutsysrootdir"/etc/group 2> /dev/null + } >> "$initdir/etc/group" + + local _systemdbinary="$systemdutildir"/systemd + + if ldd "$_systemdbinary" | grep -qw libasan; then + local _wrapper="$systemdutildir"/systemd-asan-wrapper + cat > "$initdir"/"$_wrapper" << EOF +#!/bin/sh +mount -t proc -o nosuid,nodev,noexec proc /proc +exec $_systemdbinary +EOF + chmod 755 "$initdir"/"$_wrapper" + _systemdbinary="$_wrapper" + unset _wrapper + fi + ln_r "$_systemdbinary" "/init" + ln_r "$_systemdbinary" "/sbin/init" + + unset _systemdbinary + + inst_binary true + ln_r "$(find_binary true)" "/usr/bin/loginctl" + ln_r "$(find_binary true)" "/bin/loginctl" + inst_rules \ + 70-uaccess.rules \ + 71-seat.rules \ + 73-seat-late.rules \ + 90-vconsole.rules \ + 99-systemd.rules + + for i in \ + emergency.target \ + rescue.target \ + systemd-ask-password-console.service \ + systemd-ask-password-plymouth.service; do + [[ -f "$systemdsystemunitdir"/$i ]] || continue + $SYSTEMCTL -q --root "$initdir" add-wants "$i" systemd-vconsole-setup.service + done + + mkdir -p "$initdir/etc/systemd" + # We must use a volatile journal, and we don't want rate-limiting + { + echo "[Journal]" + echo "Storage=volatile" + echo "RateLimitInterval=0" + echo "RateLimitBurst=0" + } >> "$initdir/etc/systemd/journald.conf" + + $SYSTEMCTL -q --root "$initdir" set-default multi-user.target + + # Install library file(s) + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file \ + {"tls/$_arch/",tls/,"$_arch/",}"libnss_*" + +} diff --git a/modules.d/00warpclock/module-setup.sh b/modules.d/00warpclock/module-setup.sh new file mode 100755 index 0000000..44aa177 --- /dev/null +++ b/modules.d/00warpclock/module-setup.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # hwclock does not exist on S390(x), bail out silently then + local _arch=${DRACUT_ARCH:-$(uname -m)} + [ "$_arch" = "s390" -o "$_arch" = "s390x" ] && return 1 + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries hwclock || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_hook pre-trigger 00 "$moddir/warpclock.sh" + + inst_multiple -o \ + /usr/share/zoneinfo/UTC \ + /etc/localtime \ + /etc/adjtime \ + hwclock + +} diff --git a/modules.d/00warpclock/warpclock.sh b/modules.d/00warpclock/warpclock.sh new file mode 100755 index 0000000..fb4df0b --- /dev/null +++ b/modules.d/00warpclock/warpclock.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# This file is part of dracut warpclock module. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Set the kernel's timezone and reset the system time +# if adjtime is set to LOCAL. + +if test -e /etc/adjtime; then + while read -r line; do + if test "$line" = LOCAL; then + hwclock --systz + fi + done < /etc/adjtime +fi diff --git a/modules.d/01fips/fips-boot.sh b/modules.d/01fips/fips-boot.sh new file mode 100755 index 0000000..34760e0 --- /dev/null +++ b/modules.d/01fips/fips-boot.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if ! fipsmode=$(getarg fips) || [ "$fipsmode" = "0" ]; then + rm -f -- /etc/modprobe.d/fips.conf > /dev/null 2>&1 +elif [ -z "$fipsmode" ]; then + die "FIPS mode have to be enabled by 'fips=1' not just 'fips'" +elif getarg boot= > /dev/null; then + . /sbin/fips.sh + fips_info "fips-boot: start" + if mount_boot; then + do_fips || die "FIPS integrity test failed" + fi + fips_info "fips-boot: done!" +fi diff --git a/modules.d/01fips/fips-load-crypto.sh b/modules.d/01fips/fips-load-crypto.sh new file mode 100755 index 0000000..6ef42b9 --- /dev/null +++ b/modules.d/01fips/fips-load-crypto.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if ! fipsmode=$(getarg fips) || [ "$fipsmode" = "0" ]; then + rm -f -- /etc/modprobe.d/fips.conf > /dev/null 2>&1 +elif [ -z "$fipsmode" ]; then + die "FIPS mode have to be enabled by 'fips=1' not just 'fips'" +else + . /sbin/fips.sh + fips_info "fips-load-crypto: start" + fips_load_crypto || die "FIPS integrity test failed" + fips_info "fips-load-crypto: done!" +fi diff --git a/modules.d/01fips/fips-noboot.sh b/modules.d/01fips/fips-noboot.sh new file mode 100755 index 0000000..963a034 --- /dev/null +++ b/modules.d/01fips/fips-noboot.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if ! fipsmode=$(getarg fips) || [ "$fipsmode" = "0" ]; then + rm -f -- /etc/modprobe.d/fips.conf > /dev/null 2>&1 +elif [ -z "$fipsmode" ]; then + die "FIPS mode have to be enabled by 'fips=1' not just 'fips'" +elif ! [ -f /tmp/fipsdone ]; then + . /sbin/fips.sh + fips_info "fips-noboot: start" + mount_boot + do_fips || die "FIPS integrity test failed" + fips_info "fips-noboot: done!" +fi diff --git a/modules.d/01fips/fips.sh b/modules.d/01fips/fips.sh new file mode 100755 index 0000000..2e7b7cb --- /dev/null +++ b/modules.d/01fips/fips.sh @@ -0,0 +1,195 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +# systemd lets stdout go to journal only, but the system +# has to halt when the integrity check fails to satisfy FIPS. +if [ -z "$DRACUT_SYSTEMD" ]; then + fips_info() { + info "$*" + } +else + fips_info() { + echo "$*" >&2 + } +fi + +mount_boot() { + boot=$(getarg boot=) + + if [ -n "$boot" ]; then + if [ -d /boot ] && ismounted /boot; then + boot_dev= + if command -v findmnt > /dev/null; then + boot_dev=$(findmnt -n -o SOURCE /boot) + fi + fips_info "Ignoring 'boot=$boot' as /boot is already mounted ${boot_dev:+"from '$boot_dev'"}" + return 0 + fi + + case "$boot" in + LABEL=* | UUID=* | PARTUUID=* | PARTLABEL=*) + boot="$(label_uuid_to_dev "$boot")" + ;; + /dev/*) ;; + + *) + die "You have to specify boot=<boot device> as a boot option for fips=1" + ;; + esac + + if ! [ -e "$boot" ]; then + udevadm trigger --action=add > /dev/null 2>&1 + + i=0 + while ! [ -e "$boot" ]; do + udevadm settle --exit-if-exists="$boot" + [ -e "$boot" ] && break + sleep 0.5 + i=$((i + 1)) + [ $i -gt 40 ] && break + done + fi + + [ -e "$boot" ] || return 1 + + mkdir -p /boot + fips_info "Mounting $boot as /boot" + mount -oro "$boot" /boot || return 1 + FIPS_MOUNTED_BOOT=1 + elif ! ismounted /boot && [ -d "$NEWROOT/boot" ]; then + # shellcheck disable=SC2114 + rm -fr -- /boot + ln -sf "$NEWROOT/boot" /boot + else + die "You have to specify boot=<boot device> as a boot option for fips=1" + fi +} + +do_rhevh_check() { + KERNEL=$(uname -r) + kpath=${1} + + # If we're on RHEV-H, the kernel is in /run/initramfs/live/vmlinuz0 + HMAC_SUM_ORIG=$(while read -r a _ || [ -n "$a" ]; do printf "%s\n" "$a"; done < "$NEWROOT/boot/.vmlinuz-${KERNEL}.hmac") + HMAC_SUM_CALC=$(sha512hmac "$kpath" | while read -r a _ || [ -n "$a" ]; do printf "%s\n" "$a"; done || return 1) + if [ -z "$HMAC_SUM_ORIG" ] || [ -z "$HMAC_SUM_CALC" ] || [ "${HMAC_SUM_ORIG}" != "${HMAC_SUM_CALC}" ]; then + warn "HMAC sum mismatch" + return 1 + fi + fips_info "rhevh_check OK" + return 0 +} + +nonfatal_modprobe() { + modprobe "$1" 2>&1 > /dev/stdout \ + | while read -r line || [ -n "$line" ]; do + echo "${line#modprobe: FATAL: }" >&2 + done +} + +fips_load_crypto() { + local _k + local _v + local _module + local _found + + read -d '' -r FIPSMODULES < /etc/fipsmodules + + fips_info "Loading and integrity checking all crypto modules" + mv /etc/modprobe.d/fips.conf /etc/modprobe.d/fips.conf.bak + for _module in $FIPSMODULES; do + if [ "$_module" != "tcrypt" ]; then + if ! nonfatal_modprobe "${_module}" 2> /tmp/fips.modprobe_err; then + # check if kernel provides generic algo + _found=0 + while read -r _k _ _v || [ -n "$_k" ]; do + [ "$_k" != "name" -a "$_k" != "driver" ] && continue + [ "$_v" != "$_module" ] && continue + _found=1 + break + done < /proc/crypto + [ "$_found" = "0" ] && cat /tmp/fips.modprobe_err >&2 && return 1 + fi + fi + done + mv /etc/modprobe.d/fips.conf.bak /etc/modprobe.d/fips.conf + + fips_info "Self testing crypto algorithms" + modprobe tcrypt || return 1 + rmmod tcrypt +} + +do_fips() { + KERNEL=$(uname -r) + + if ! getarg rd.fips.skipkernel > /dev/null; then + + fips_info "Checking integrity of kernel" + if [ -e "/run/initramfs/live/vmlinuz0" ]; then + do_rhevh_check /run/initramfs/live/vmlinuz0 || return 1 + elif [ -e "/run/initramfs/live/isolinux/vmlinuz0" ]; then + do_rhevh_check /run/initramfs/live/isolinux/vmlinuz0 || return 1 + elif [ -e "/run/install/repo/images/pxeboot/vmlinuz" ]; then + # This is a boot.iso with the .hmac inside the install.img + do_rhevh_check /run/install/repo/images/pxeboot/vmlinuz || return 1 + else + BOOT_IMAGE="$(getarg BOOT_IMAGE)" + + # On s390x, BOOT_IMAGE isn't a path but an integer representing the + # entry number selected. Let's try the root of /boot first, and + # otherwise fallback to trying to parse the BLS entries if it's a + # BLS-based system. + if [ "$(uname -m)" = s390x ]; then + if [ -e "/boot/vmlinuz-${KERNEL}" ]; then + BOOT_IMAGE="vmlinuz-${KERNEL}" + elif [ -d /boot/loader/entries ]; then + bls=$(find /boot/loader/entries -name '*.conf' | sort -rV | sed -n "$((BOOT_IMAGE + 1))p") + if [ -e "${bls}" ]; then + BOOT_IMAGE=$(grep ^linux "${bls}" | cut -d' ' -f2) + fi + fi + fi + + # Trim off any leading GRUB boot device (e.g. ($root) ) + BOOT_IMAGE="$(echo "${BOOT_IMAGE}" | sed 's/^(.*)//')" + + BOOT_IMAGE_NAME="${BOOT_IMAGE##*/}" + BOOT_IMAGE_PATH="${BOOT_IMAGE%"${BOOT_IMAGE_NAME}"}" + + if [ -z "$BOOT_IMAGE_NAME" ]; then + BOOT_IMAGE_NAME="vmlinuz-${KERNEL}" + elif ! [ -e "/boot/${BOOT_IMAGE_PATH}/${BOOT_IMAGE_NAME}" ]; then + #if /boot is not a separate partition BOOT_IMAGE might start with /boot + BOOT_IMAGE_PATH=${BOOT_IMAGE_PATH#"/boot"} + #on some architectures BOOT_IMAGE does not contain path to kernel + #so if we can't find anything, let's treat it in the same way as if it was empty + if ! [ -e "/boot/${BOOT_IMAGE_PATH}/${BOOT_IMAGE_NAME}" ]; then + BOOT_IMAGE_NAME="vmlinuz-${KERNEL}" + BOOT_IMAGE_PATH="" + fi + fi + + BOOT_IMAGE_HMAC="/boot/${BOOT_IMAGE_PATH}/.${BOOT_IMAGE_NAME}.hmac" + if ! [ -e "${BOOT_IMAGE_HMAC}" ]; then + warn "${BOOT_IMAGE_HMAC} does not exist" + return 1 + fi + + (cd "${BOOT_IMAGE_HMAC%/*}" && sha512hmac -c "${BOOT_IMAGE_HMAC}") || return 1 + fi + fi + + fips_info "All initrd crypto checks done" + + : > /tmp/fipsdone + + if [ "$FIPS_MOUNTED_BOOT" = 1 ]; then + fips_info "Unmounting /boot" + umount /boot > /dev/null 2>&1 + else + fips_info "Not unmounting /boot" + fi + + return 0 +} diff --git a/modules.d/01fips/module-setup.sh b/modules.d/01fips/module-setup.sh new file mode 100755 index 0000000..0e47c84 --- /dev/null +++ b/modules.d/01fips/module-setup.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + local _fipsmodules _mod _bootfstype + if [[ -f "${srcmods}/modules.fips" ]]; then + read -d '' -r _fipsmodules < "${srcmods}/modules.fips" + else + _fipsmodules="" + + # Hashes: + _fipsmodules+="sha1 sha224 sha256 sha384 sha512 " + _fipsmodules+="sha3-224 sha3-256 sha3-384 sha3-512 " + _fipsmodules+="crc32c crct10dif ghash " + + # Ciphers: + _fipsmodules+="cipher_null des3_ede aes cfb dh ecdh " + + # Modes/templates: + _fipsmodules+="ecb cbc ctr xts gcm ccm authenc hmac cmac ofb cts " + + # Compression algs: + _fipsmodules+="deflate lzo zlib " + + # PRNG algs: + _fipsmodules+="ansi_cprng " + + # Misc: + _fipsmodules+="aead cryptomgr tcrypt crypto_user " + fi + + # shellcheck disable=SC2174 + mkdir -m 0755 -p "${initdir}/etc/modprobe.d" + + for _mod in $_fipsmodules; do + if hostonly='' instmods -c -s "$_mod"; then + echo "$_mod" >> "${initdir}/etc/fipsmodules" + echo "blacklist $_mod" >> "${initdir}/etc/modprobe.d/fips.conf" + fi + done + + # with hostonly_default_device fs module for /boot is not installed by default + if [[ $hostonly ]] && [[ $hostonly_default_device == "no" ]]; then + _bootfstype=$(find_mp_fstype /boot) + if [[ -n $_bootfstype ]]; then + hostonly='' instmods "$_bootfstype" + else + dwarning "Can't determine fs type for /boot, FIPS check may fail." + fi + fi +} + +# called by dracut +install() { + inst_hook pre-pivot 00 "$moddir/fips-boot.sh" + inst_hook pre-pivot 01 "$moddir/fips-noboot.sh" + inst_hook pre-udev 01 "$moddir/fips-load-crypto.sh" + inst_script "$moddir/fips.sh" /sbin/fips.sh + + inst_multiple sha512hmac rmmod insmod mount uname umount grep sed cut find sort + + inst_simple /etc/system-fips + [ -c "${initdir}"/dev/random ] || mknod "${initdir}"/dev/random c 1 8 \ + || { + dfatal "Cannot create /dev/random" + dfatal "To create an initramfs with fips support, dracut has to run as root" + return 1 + } + [ -c "${initdir}"/dev/urandom ] || mknod "${initdir}"/dev/urandom c 1 9 \ + || { + dfatal "Cannot create /dev/urandom" + dfatal "To create an initramfs with fips support, dracut has to run as root" + return 1 + } +} diff --git a/modules.d/01systemd-ac-power/99-initrd-power-targets.rules b/modules.d/01systemd-ac-power/99-initrd-power-targets.rules new file mode 100644 index 0000000..1490a22 --- /dev/null +++ b/modules.d/01systemd-ac-power/99-initrd-power-targets.rules @@ -0,0 +1,3 @@ +# This file is part of dracut systemd ac power module +SUBSYSTEM=="power_supply", KERNEL=="AC", ATTR{online}=="0", RUN+="/usr/sbin/systemctl start initrd-on-battery-power.target" +SUBSYSTEM=="power_supply", KERNEL=="AC", ATTR{online}=="1", RUN+="/usr/sbin/systemctl start initrd-on-ac-power.target" diff --git a/modules.d/01systemd-ac-power/initrd-on-ac-power.target b/modules.d/01systemd-ac-power/initrd-on-ac-power.target new file mode 100644 index 0000000..73a9978 --- /dev/null +++ b/modules.d/01systemd-ac-power/initrd-on-ac-power.target @@ -0,0 +1,8 @@ +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +[Unit] +Description=Initial RAM Disk On AC Power +ConditionPathExists=/usr/lib/initrd-release +DefaultDependencies=no +StopWhenUnneeded=yes diff --git a/modules.d/01systemd-ac-power/initrd-on-battery-power.target b/modules.d/01systemd-ac-power/initrd-on-battery-power.target new file mode 100644 index 0000000..fdd54a5 --- /dev/null +++ b/modules.d/01systemd-ac-power/initrd-on-battery-power.target @@ -0,0 +1,8 @@ +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +[Unit] +Description=Initial RAM Disk On Battery Power +ConditionPathExists=/usr/lib/initrd-release +DefaultDependencies=no +StopWhenUnneeded=yes diff --git a/modules.d/01systemd-ac-power/module-setup.sh b/modules.d/01systemd-ac-power/module-setup.sh new file mode 100755 index 0000000..c4e7fdd --- /dev/null +++ b/modules.d/01systemd-ac-power/module-setup.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_rules "$moddir/99-initrd-power-targets.rules" + inst systemd-ac-power + inst_simple "$moddir/initrd-on-ac-power.target" "$systemdsystemunitdir/initrd-on-ac-power.target" + inst_simple "$moddir/initrd-on-battery-power.target" "$systemdsystemunitdir/initrd-on-battery-power.target" + +} diff --git a/modules.d/01systemd-ask-password/module-setup.sh b/modules.d/01systemd-ask-password/module-setup.sh new file mode 100755 index 0000000..6e78075 --- /dev/null +++ b/modules.d/01systemd-ask-password/module-setup.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries \ + systemd-ask-password \ + systemd-tty-ask-password-agent \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) for the module in the initramfs. +install() { + + inst_multiple -o \ + "$systemdsystemunitdir"/systemd-ask-password-console.path \ + "$systemdsystemunitdir"/systemd-ask-password-console.service \ + "$systemdsystemunitdir"/multi-user.target.wants/systemd-ask-password-wall.path \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-ask-password-console.path \ + systemd-ask-password \ + systemd-tty-ask-password-agent + + # Enable the systemd type service unit for systemd-ask-password. + $SYSTEMCTL -q --root "$initdir" enable systemd-ask-password-console.service + + # Install systemd-ask-password plymouth units if plymouth is enabled. + if dracut_module_included "plymouth"; then + inst_multiple -o \ + "$systemdsystemunitdir"/systemd-ask-password-plymouth.path \ + "$systemdsystemunitdir"/systemd-ask-password-plymouth.service + + $SYSTEMCTL -q --root "$initdir" enable systemd-ask-password-plymouth.service + fi + + # Uncomment this section if the usecase for wall module in the initramfs arises. + # Install systemd-ask-password wall units if <wall module> is enabled. + #if dracut_module_included "<wall module>"; then + # inst_multiple -o \ + # $systemdsystemunitdir/systemd-ask-password-wall.path \ + # $systemdsystemunitdir/systemd-ask-password-wall.service \ + # $systemdsystemunitdir/multi-user.target.wants/systemd-ask-password-wall.path \ + # + # $SYSTEMCTL -q --root "$initdir" enable systemd-ask-password-wall.service + #fi +} diff --git a/modules.d/01systemd-coredump/module-setup.sh b/modules.d/01systemd-coredump/module-setup.sh new file mode 100755 index 0000000..69ec966 --- /dev/null +++ b/modules.d/01systemd-coredump/module-setup.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries \ + coredumpctl \ + "$systemdutildir"/systemd-coredump \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on the systemd module. + echo systemd-journald systemd-sysctl systemd-sysusers + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_dir /var/lib/systemd/coredump + inst_multiple -o \ + "$sysctld"/50-coredump.conf \ + "$systemdutildir"/coredump.conf \ + "$systemdutildir"/systemd-coredump \ + "$systemdsystemunitdir"/systemd-coredump.socket \ + "$systemdsystemunitdir"/systemd-coredump@.service \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-coredump.socket \ + "$sysusers"/systemd-coredump.conf \ + coredumpctl + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdutilconfdir"/coredump.conf \ + "$systemdsystemconfdir/coredump.conf.d/*.conf" \ + "$systemdsystemconfdir"/systemd-coredump.socket \ + "$systemdsystemconfdir/systemd-coredump.socket.d/*.conf" \ + "$systemdsystemconfdir"/systemd-coredump@.service \ + "$systemdsystemconfdir/systemd-coredump@.service.d/*.conf" \ + "$systemdsystemconfdir"/sockets.target.wants/systemd-coredump.socket \ + "$sysusersconfdir"/systemd-coredump.conf + fi +} diff --git a/modules.d/01systemd-creds/module-setup.sh b/modules.d/01systemd-creds/module-setup.sh new file mode 100755 index 0000000..1b66519 --- /dev/null +++ b/modules.d/01systemd-creds/module-setup.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries systemd-creds || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + local deps + + # This module has external dependency on other module(s). + deps="systemd" + systemd-creds -q has-tpm2 && deps+=" tpm2-tss" + echo "$deps" + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "/usr/lib/credstore/*" \ + "/usr/lib/credstore.encrypted/*" \ + "$tmpfilesdir/credstore.conf" \ + systemd-creds + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "/etc/credstore/*" \ + "/etc/credstore.encrypted/*" + fi + +} diff --git a/modules.d/01systemd-hostnamed/99-systemd-networkd-dracut.conf b/modules.d/01systemd-hostnamed/99-systemd-networkd-dracut.conf new file mode 100644 index 0000000..2e53e2d --- /dev/null +++ b/modules.d/01systemd-hostnamed/99-systemd-networkd-dracut.conf @@ -0,0 +1,6 @@ +# This file is part of dracut systemd-hostnamed module. + +[Service] +User=systemd-network +Group=systemd-hostname +AmbientCapabilities=CAP_SYS_ADMIN diff --git a/modules.d/01systemd-hostnamed/module-setup.sh b/modules.d/01systemd-hostnamed/module-setup.sh new file mode 100755 index 0000000..2a1fa02 --- /dev/null +++ b/modules.d/01systemd-hostnamed/module-setup.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries \ + hostnamectl \ + "$systemdutildir"/systemd-hostnamed \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo dbus systemd-sysusers + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_simple "$moddir/systemd-hostname-dracut.conf" "$sysusers/systemd-hostname-dracut.conf" + inst_simple "$moddir/org.freedesktop.hostname1_dracut.conf" "$dbussystem/org.freedesktop.hostname1_dracut.conf" + inst_simple "$moddir/99-systemd-networkd-dracut.conf" "$systemdsystemunitdir/systemd-hostnamed.service.d/99-systemd-networkd-dracut.conf" + + inst_multiple -o \ + "$dbussystem"/org.freedesktop.hostname1.conf \ + "$dbussystemservices"/org.freedesktop.hostname1.service \ + "$systemdutildir"/systemd-hostnamed \ + "$systemdsystemunitdir"/systemd-hostnamed.service \ + "$systemdsystemunitdir/systemd-hostnamed.service.d/*.conf" \ + "$systemdsystemunitdir"/dbus-org.freedesktop.hostname1.service \ + hostnamectl + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + /etc/hostname \ + "$systemdsystemconfdir"/systemd-hostnamed.service \ + "$systemdsystemconfdir/systemd-hostnamed.service.d/*.conf" + fi +} diff --git a/modules.d/01systemd-hostnamed/org.freedesktop.hostname1_dracut.conf b/modules.d/01systemd-hostnamed/org.freedesktop.hostname1_dracut.conf new file mode 100644 index 0000000..4410c1c --- /dev/null +++ b/modules.d/01systemd-hostnamed/org.freedesktop.hostname1_dracut.conf @@ -0,0 +1,19 @@ +<?xml version="1.0"?> <!--*-nxml-*--> +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> + +<!-- + + This file is part of dracut systemd-hostnamed module. + +--> + +<busconfig> + + <policy group="systemd-hostname"> + <allow own="org.freedesktop.hostname1"/> + <allow send_destination="org.freedesktop.hostname1"/> + <allow receive_sender="org.freedesktop.hostname1"/> + </policy> + +</busconfig> diff --git a/modules.d/01systemd-hostnamed/systemd-hostname-dracut.conf b/modules.d/01systemd-hostnamed/systemd-hostname-dracut.conf new file mode 100644 index 0000000..3acb748 --- /dev/null +++ b/modules.d/01systemd-hostnamed/systemd-hostname-dracut.conf @@ -0,0 +1,2 @@ +# This file is part of dracut systemd-hostnamed module. +g systemd-hostname - diff --git a/modules.d/01systemd-initrd/module-setup.sh b/modules.d/01systemd-initrd/module-setup.sh new file mode 100755 index 0000000..f65041f --- /dev/null +++ b/modules.d/01systemd-initrd/module-setup.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# called by dracut +check() { + [[ $mount_needs ]] && return 1 + + return 0 +} + +# called by dracut +depends() { + echo "systemd" +} + +installkernel() { + return 0 +} + +# called by dracut +install() { + inst_multiple -o \ + "$systemdsystemunitdir"/initrd.target \ + "$systemdsystemunitdir"/initrd-fs.target \ + "$systemdsystemunitdir"/initrd-root-device.target \ + "$systemdsystemunitdir"/initrd-root-fs.target \ + "$systemdsystemunitdir"/initrd-usr-fs.target \ + "$systemdsystemunitdir"/initrd-switch-root.target \ + "$systemdsystemunitdir"/initrd-switch-root.service \ + "$systemdsystemunitdir"/initrd-cleanup.service \ + "$systemdsystemunitdir"/initrd-udevadm-cleanup-db.service \ + "$systemdsystemunitdir"/initrd-parse-etc.service + + $SYSTEMCTL -q --root "$initdir" set-default initrd.target +} diff --git a/modules.d/01systemd-integritysetup/module-setup.sh b/modules.d/01systemd-integritysetup/module-setup.sh new file mode 100755 index 0000000..3d17640 --- /dev/null +++ b/modules.d/01systemd-integritysetup/module-setup.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries \ + "$systemdutildir"/systemd-integritysetup \ + "$systemdutildir"/system-generators/systemd-integritysetup-generator \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd dm + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +installkernel() { + instmods dm-integrity +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "$systemdutildir"/systemd-integritysetup \ + "$systemdutildir"/system-generators/systemd-integritysetup-generator \ + "$systemdsystemunitdir"/integritysetup-pre.target \ + "$systemdsystemunitdir"/integritysetup.target \ + "$systemdsystemunitdir"/sysinit.target.wants/integritysetup.target + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + /etc/integritytab \ + "$systemdsystemconfdir"/integritysetup.target \ + "$systemdsystemconfdir/integritysetup.target.wants/*.target" \ + "$systemdsystemconfdir"/integritysetup-pre.target \ + "$systemdsystemconfdir/integritysetup-pre.target.wants/*.target" \ + "$systemdsystemconfdir"/sysinit.target.wants/integritysetup.target \ + "$systemdsystemconfdir/sysinit.target.wants/integritysetup.target.wants/*.target" + fi + + # Install required libraries. + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file {"tls/$_arch/",tls/,"$_arch/",}"libcryptsetup.so.*" + +} diff --git a/modules.d/01systemd-journald/initrd.conf b/modules.d/01systemd-journald/initrd.conf new file mode 100644 index 0000000..1246e05 --- /dev/null +++ b/modules.d/01systemd-journald/initrd.conf @@ -0,0 +1,9 @@ +# This file is part of dracut systemd-journal module. +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Dracut requires volatile journal without rate-limiting + +[Journal] +Storage=volatile +RateLimitInterval=0 +RateLimitBurst=0 diff --git a/modules.d/01systemd-journald/module-setup.sh b/modules.d/01systemd-journald/module-setup.sh new file mode 100755 index 0000000..3cf2a1a --- /dev/null +++ b/modules.d/01systemd-journald/module-setup.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries \ + journalctl \ + "$systemdutildir"/systemd-journald \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd-sysusers + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_simple "$moddir/initrd.conf" "$systemdutildir/journald.conf.d/initrd.conf" + + inst_multiple -o \ + "$systemdutildir"/journald.conf \ + "$systemdutildir/journald.conf.d/*.conf" \ + "$systemdutildir"/systemd-journald \ + "$systemdsystemunitdir"/systemd-journald.service \ + "$systemdsystemunitdir"/systemd-journald.socket \ + "$systemdsystemunitdir"/systemd-journald@.service \ + "$systemdsystemunitdir"/systemd-journald@.socket \ + "$systemdsystemunitdir"/systemd-journald-audit.socket \ + "$systemdsystemunitdir"/systemd-journald-dev-log.socket \ + "$systemdsystemunitdir"/systemd-journald-varlink@.socket \ + "$systemdsystemunitdir"/systemd-journal-catalog-update.service \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-journald-audit.socket \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-journald-dev-log.socket \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-journald.socket \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-journald.service \ + "$sysusers"/systemd-journal.conf \ + journalctl + + # Install library file(s) + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file \ + {"tls/$_arch/",tls/,"$_arch/",}"liblz4.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libzstd.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"liblzma.so.*" + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdutilconfdir"/journald.conf \ + "$systemdutilconfdir/journald.conf.d/*.conf" \ + "$systemdsystemconfdir"/systemd-journald.service \ + "$systemdsystemconfdir/systemd-journald.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-journal-catalog-update.service \ + "$systemdsystemconfdir/systemd-journal-catalog-update.service.d/*.conf" \ + "$sysusersconfdir"/systemd-journal.conf + fi + +} diff --git a/modules.d/01systemd-ldconfig/module-setup.sh b/modules.d/01systemd-ldconfig/module-setup.sh new file mode 100755 index 0000000..3a5842a --- /dev/null +++ b/modules.d/01systemd-ldconfig/module-setup.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries ldconfig || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + /etc/ld.so.cache \ + /etc/ld.so.conf \ + "/etc/ld.so.conf.d/*.conf" \ + "$systemdsystemunitdir"/ldconfig.service \ + "$systemdsystemunitdir/ldconfig.service.d/*.conf" \ + "$systemdsystemunitdir"/sysinit.target.wants/ldconfig.service \ + ldconfig + + # Install required libraries. + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file {"tls/$_arch/",tls/,"$_arch/",}"ld.so" + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdsystemconfdir"/ldconfig.service \ + "$systemdsystemconfdir/ldconfig.service.d/*.conf" + fi + +} diff --git a/modules.d/01systemd-modules-load/module-setup.sh b/modules.d/01systemd-modules-load/module-setup.sh new file mode 100755 index 0000000..f9adfea --- /dev/null +++ b/modules.d/01systemd-modules-load/module-setup.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries "$systemdutildir"/systemd-modules-load || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "$modulesload/*.conf" \ + "$systemdutildir"/systemd-modules-load \ + "$systemdsystemunitdir"/systemd-modules-load.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-modules-load.service + + # Enable systemd type unit(s) + $SYSTEMCTL -q --root "$initdir" enable systemd-modules-load.service + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$modulesloadconfdir/*.conf" \ + "$systemdsystemconfdir"/systemd-modules-load.service \ + "$systemdsystemconfdir/systemd-modules-load.service.d/*.conf" + fi + +} diff --git a/modules.d/01systemd-networkd/module-setup.sh b/modules.d/01systemd-networkd/module-setup.sh new file mode 100755 index 0000000..7bbf83c --- /dev/null +++ b/modules.d/01systemd-networkd/module-setup.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + [[ $mount_needs ]] && return 1 + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries ip networkctl \ + "$systemdutildir"/systemd-networkd \ + "$systemdutildir"/systemd-network-generator \ + "$systemdutildir"/systemd-networkd-wait-online \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo dbus kernel-network-modules systemd-sysusers + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "$tmpfilesdir"/systemd-network.conf \ + "$dbussystem"/org.freedesktop.network1.conf \ + "$dbussystemservices"/org.freedesktop.network1.service \ + "$systemdutildir"/networkd.conf \ + "$systemdutildir/networkd.conf.d/*.conf" \ + "$systemdutildir"/systemd-networkd \ + "$systemdutildir"/systemd-network-generator \ + "$systemdutildir"/systemd-networkd-wait-online \ + "$systemdnetwork"/80-6rd-tunnel.network \ + "$systemdnetwork"/80-container-host0.network \ + "$systemdnetwork"/80-container-vb.network \ + "$systemdnetwork"/80-container-ve.network \ + "$systemdnetwork"/80-container-vz.network \ + "$systemdnetwork"/80-vm-vt.network \ + "$systemdnetwork"/80-wifi-adhoc.network \ + "$systemdnetwork"/99-default.link \ + "$systemdsystemunitdir"/systemd-networkd.service \ + "$systemdsystemunitdir"/systemd-networkd.socket \ + "$systemdsystemunitdir"/systemd-network-generator.service \ + "$systemdsystemunitdir"/systemd-networkd-wait-online.service \ + "$systemdsystemunitdir"/systemd-networkd-wait-online@.service \ + "$systemdsystemunitdir"/systemd-network-generator.service \ + "$sysusers"/systemd-network.conf \ + networkctl ip + + # Enable systemd type units + for i in \ + systemd-networkd.service \ + systemd-networkd.socket \ + systemd-network-generator.service \ + systemd-networkd-wait-online.service; do + $SYSTEMCTL -q --root "$initdir" enable "$i" + done + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdutilconfdir"/networkd.conf \ + "$systemdutilconfdir/networkd.conf.d/*.conf" \ + "$systemdnetworkconfdir/*" \ + "$systemdsystemconfdir"/systemd-networkd.service \ + "$systemdsystemconfdir/systemd-networkd.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-networkd.socket \ + "$systemdsystemconfdir/systemd-networkd.socket.d/*.conf" \ + "$systemdsystemconfdir"/systemd-network-generator.service \ + "$systemdsystemconfdir/systemd-network-generator.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-networkd-wait-online.service \ + "$systemdsystemconfdir/systemd-networkd-wait-online.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-networkd-wait-online@.service \ + "$systemdsystemconfdir/systemd-networkd-wait-online@.service.d/*.conf" \ + "$sysusersconfdir"/systemd-network.conf + fi +} diff --git a/modules.d/01systemd-pcrphase/module-setup.sh b/modules.d/01systemd-pcrphase/module-setup.sh new file mode 100755 index 0000000..fa960a4 --- /dev/null +++ b/modules.d/01systemd-pcrphase/module-setup.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries "$systemdutildir"/systemd-pcrphase || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd tpm2-tss + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "$systemdutildir"/systemd-pcrphase \ + "$systemdsystemunitdir"/systemd-pcrphase-initrd.service \ + "$systemdsystemunitdir/systemd-pcrphase-initrd.service.d/*.conf" \ + "$systemdsystemunitdir"/initrd.target.wants/systemd-pcrphase-initrd.service + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdsystemconfdir"/systemd-pcrphase-initrd.service \ + "$systemdsystemconfdir/systemd-pcrphase-initrd.service.d/*.conf" \ + "$systemdsystemconfdir"/initrd.target.wants/systemd-pcrphase-initrd.service + fi + +} diff --git a/modules.d/01systemd-portabled/module-setup.sh b/modules.d/01systemd-portabled/module-setup.sh new file mode 100755 index 0000000..ccbc11f --- /dev/null +++ b/modules.d/01systemd-portabled/module-setup.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries \ + portablectl \ + "$systemdutildir"/systemd-portabled \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo dbus + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install kernel module(s). +installkernel() { + instmods loop squashfs +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + # It's intended to work only with raw binary disk images contained in + # regular files, but not with directory trees. + local _nonraw + _nonraw=$(portablectl --no-pager --no-legend list | grep -v " raw " | cut -d ' ' -f1 | tr '\n' ' ') + if [ -n "$_nonraw" ]; then + dwarn "systemd-portabled: this module only installs raw disk images in the initramfs; skipping: $_nonraw" + fi + + inst_multiple -o \ + "/var/lib/portables/*.raw" \ + "/usr/lib/portables/*.raw" \ + "$tmpfilesdir/portables.conf" \ + "$dbussystem"/org.freedesktop.portable1.conf \ + "$dbussystemservices"/org.freedesktop.portable1.service \ + "$systemdutildir"/systemd-portabled \ + "$systemdutildir/portable/profile/default/*.conf" \ + "$systemdutildir/portable/profile/nonetwork/*.conf" \ + "$systemdutildir/portable/profile/strict/*.conf" \ + "$systemdutildir/portable/profile/trusted/*.conf" \ + "$systemdsystemunitdir"/systemd-portabled.service \ + "$systemdsystemunitdir/systemd-portabled.service.d/*.conf" \ + "$systemdsystemunitdir"/dbus-org.freedesktop.portable1.service \ + portablectl + + # The existence of this file is required + touch "$initdir"/etc/resolv.conf + + # Enable systemd type unit(s) + $SYSTEMCTL -q --root "$initdir" add-wants initrd.target systemd-portabled.service + $SYSTEMCTL -q --root "$initdir" enable systemd-portabled.service + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "/etc/portables/*.raw" \ + "$systemdutilconfdir/system.attached/*" \ + "$systemdutilconfdir/system.attached/*/*" \ + "$systemdutilconfdir/portable/profile/default/*.conf" \ + "$systemdutilconfdir/portable/profile/nonetwork/*.conf" \ + "$systemdutilconfdir/portable/profile/strict/*.conf" \ + "$systemdutilconfdir/portable/profile/trusted/*.conf" \ + "$systemdsystemconfdir"/systemd-portabled.service \ + "$systemdsystemconfdir/systemd-portabled.service.d/*.conf" + fi + +} diff --git a/modules.d/01systemd-pstore/module-setup.sh b/modules.d/01systemd-pstore/module-setup.sh new file mode 100755 index 0000000..67034bb --- /dev/null +++ b/modules.d/01systemd-pstore/module-setup.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries "$systemdutildir"/systemd-pstore || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install kernel module(s). +installkernel() { + instmods efi-pstore +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_dir /var/lib/systemd/pstore + inst_multiple -o \ + "$tmpfilesdir/systemd-pstore.conf" \ + "$systemdutildir"/systemd-pstore \ + "$systemdsystemunitdir"/systemd-pstore.service \ + "$systemdsystemunitdir/systemd-pstore.service.d/*.conf" + + # Enable systemd type unit(s) + $SYSTEMCTL -q --root "$initdir" enable systemd-pstore.service + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdutilconfdir"/pstore.conf \ + "$systemdutilconfdir/pstore.conf.d/*.conf" \ + "$systemdsystemconfdir"/systemd-pstore.service \ + "$systemdsystemconfdir/systemd-pstore.service.d/*.conf" + fi + +} diff --git a/modules.d/01systemd-repart/module-setup.sh b/modules.d/01systemd-repart/module-setup.sh new file mode 100755 index 0000000..19b7e73 --- /dev/null +++ b/modules.d/01systemd-repart/module-setup.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries systemd-repart || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) for the module in the initramfs. +install() { + + inst_multiple -o \ + "/usr/lib/repart.d/*.conf" \ + "$systemdsystemunitdir"/systemd-repart.service \ + "$systemdsystemunitdir"/initrd-root-fs.target.wants/systemd-repart.service \ + systemd-repart + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "/etc/repart.d/*.conf" \ + "$systemdsystemconfdir"/systemd-repart.service \ + "$systemdsystemconfdir/systemd-repart.service.d/*.conf" + fi +} diff --git a/modules.d/01systemd-resolved/module-setup.sh b/modules.d/01systemd-resolved/module-setup.sh new file mode 100755 index 0000000..0c2e8c2 --- /dev/null +++ b/modules.d/01systemd-resolved/module-setup.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries \ + resolvectl \ + "$systemdutildir"/systemd-resolved \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo dbus systemd-sysusers + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_simple "$moddir/resolved-tmpfile-dracut.conf" "$tmpfilesdir/resolved-tmpfile-dracut.conf" + + inst_multiple -o \ + "$dbussystem"/org.freedesktop.resolve1.conf \ + "$dbussystemservices"/org.freedesktop.resolve1.service \ + "$systemdutildir"/resolv.conf \ + "$systemdutildir"/resolved.conf \ + "$systemdutildir/resolved.conf.d/*.conf" \ + "$systemdutildir"/systemd-resolved \ + "$systemdsystemunitdir"/systemd-resolved.service \ + "$systemdsystemunitdir/systemd-resolved.service.d/*.conf" \ + "$sysusers"/systemd-resolve.conf \ + resolvectl + + # Enable systemd type unit(s) + $SYSTEMCTL -q --root "$initdir" enable systemd-resolved.service + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdutilconfdir"/resolved.conf \ + "$systemdutilconfdir/resolved.conf.d/*.conf" \ + "$systemdsystemconfdir"/systemd-resolved.service \ + "$systemdsystemconfdir/systemd-resolved.service.d/*.conf" \ + "$sysusersconfdir"/systemd-resolve.conf + fi +} diff --git a/modules.d/01systemd-resolved/resolved-tmpfile-dracut.conf b/modules.d/01systemd-resolved/resolved-tmpfile-dracut.conf new file mode 100644 index 0000000..6b7daa9 --- /dev/null +++ b/modules.d/01systemd-resolved/resolved-tmpfile-dracut.conf @@ -0,0 +1,2 @@ +# This file is part of dracut systemd-resolved module. +L! /etc/resolv.conf - - - - ../run/systemd/resolve/stub-resolv.conf diff --git a/modules.d/01systemd-sysctl/module-setup.sh b/modules.d/01systemd-sysctl/module-setup.sh new file mode 100755 index 0000000..d0eb779 --- /dev/null +++ b/modules.d/01systemd-sysctl/module-setup.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries "$systemdutildir"/systemd-sysctl || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd-modules-load + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) for the module in the initramfs. +install() { + + inst_multiple -o \ + "$sysctld/*.conf" \ + "$systemdsystemunitdir"/systemd-sysctl.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-sysctl.service \ + "$systemdutildir"/systemd-sysctl + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + /etc/sysctl.conf \ + "$sysctlconfdir/*.conf" \ + "$systemdsystemconfdir"/systemd-sysctl.service \ + "$systemdsystemconfdir/systemd-sysctl.service.d/*.conf" + fi + + # Enable the systemd type service unit for sysctl. + $SYSTEMCTL -q --root "$initdir" enable systemd-sysctl.service + +} diff --git a/modules.d/01systemd-sysext/module-setup.sh b/modules.d/01systemd-sysext/module-setup.sh new file mode 100755 index 0000000..b95a480 --- /dev/null +++ b/modules.d/01systemd-sysext/module-setup.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries systemd-sysext || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "/usr/lib/extensions/*" \ + "/usr/lib/extension-release.d/extension-release.*" \ + "$systemdsystemunitdir"/systemd-sysext.service \ + "$systemdsystemunitdir/systemd-sysext.service.d/*.conf" \ + systemd-sysext + + # Enable systemd type unit(s) + $SYSTEMCTL -q --root "$initdir" enable systemd-sysext.service + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "/etc/extensions/*" \ + "$systemdsystemconfdir"/systemd-sysext.service \ + "$systemdsystemconfdir/systemd-sysext.service.d/*.conf" + fi + +} diff --git a/modules.d/01systemd-sysusers/module-setup.sh b/modules.d/01systemd-sysusers/module-setup.sh new file mode 100755 index 0000000..4ec48dc --- /dev/null +++ b/modules.d/01systemd-sysusers/module-setup.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries systemd-sysusers || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_simple "$moddir/sysusers-dracut.conf" "$systemdsystemunitdir/systemd-sysusers.service.d/sysusers-dracut.conf" + + inst_multiple -o \ + "$sysusers"/basic.conf \ + "$sysusers"/systemd.conf \ + "$systemdsystemunitdir"/systemd-sysusers.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-sysusers.service \ + systemd-sysusers + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$sysusersconfdir"/basic.conf \ + "$sysusersconfdir"/systemd.conf \ + "$systemdsystemconfdir"/systemd-sysusers.service \ + "$systemdsystemconfdir/systemd-sysusers.service.d/*.conf" + fi + +} diff --git a/modules.d/01systemd-sysusers/sysusers-dracut.conf b/modules.d/01systemd-sysusers/sysusers-dracut.conf new file mode 100644 index 0000000..9b13364 --- /dev/null +++ b/modules.d/01systemd-sysusers/sysusers-dracut.conf @@ -0,0 +1,2 @@ +[Unit] +ConditionNeedsUpdate= diff --git a/modules.d/01systemd-timedated/module-setup.sh b/modules.d/01systemd-timedated/module-setup.sh new file mode 100755 index 0000000..5e9c5ca --- /dev/null +++ b/modules.d/01systemd-timedated/module-setup.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries \ + timedatectl \ + "$systemdutildir"/systemd-timedated \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo dbus + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "$dbussystem"/org.freedesktop.timedate1.conf \ + "$dbussystemservices"/org.freedesktop.timedate1.service \ + "$systemdutildir"/systemd-timedated \ + "$systemdsystemunitdir"/systemd-timedated.service \ + "$systemdsystemunitdir"/dbus-org.freedesktop.timedate1.service \ + timedatectl + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdsystemconfdir"/systemd-timedated.service \ + "$systemdsystemconfdir/systemd-timedated.service.d/*.conf" + fi +} diff --git a/modules.d/01systemd-timesyncd/module-setup.sh b/modules.d/01systemd-timesyncd/module-setup.sh new file mode 100755 index 0000000..a2c6754 --- /dev/null +++ b/modules.d/01systemd-timesyncd/module-setup.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries \ + "$systemdutildir"/systemd-timesyncd \ + "$systemdutildir"/systemd-time-wait-sync \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo dbus systemd-sysusers systemd-timedated + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + # Enable this if networkd ( not the module ) is disabled at build time and you want to use timesyncd + # inst_simple "$moddir/timesyncd-tmpfile-dracut.conf" "$tmpfilesdir/timesyncd-tmpfile-dracut.conf" + + inst_multiple -o \ + "$dbussystem"/org.freedesktop.timesync1.conf \ + "$dbussystemservices"/org.freedesktop.timesync1.service \ + "$systemdntpunits/*.list" \ + "$systemdutildir"/systemd-timesyncd \ + "$systemdutildir"/systemd-time-wait-sync \ + "$systemdutildir/timesyncd.conf.d/*.conf" \ + "$systemdsystemunitdir"/systemd-timesyncd.service \ + "$systemdsystemunitdir/systemd-timesyncd.service.d/*.conf" \ + "$systemdsystemunitdir"/systemd-time-wait-sync.service \ + "$systemdsystemunitdir/systemd-time-wait-sync.service.d/*.conf" \ + "$sysusers"/systemd-timesync.conf + + # Enable systemd type unit(s) + for i in \ + systemd-timesyncd.service \ + systemd-time-wait-sync.service; do + $SYSTEMCTL -q --root "$initdir" enable "$i" + done + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$systemdntpunitsconfdir/*.list" \ + "$systemdutilconfdir"/timesyncd.conf \ + "$systemdutilconfdir/timesyncd.conf.d/*.conf" \ + "$systemdsystemconfdir"/systemd-timesyncd.service \ + "$systemdsystemconfdir/systemd-timesyncd.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-time-wait-sync.service \ + "$systemdsystemconfdir/systemd-time-wait-sync.service.d/*.conf" \ + "$sysusersconfdir"/systemd-timesync.conf + fi +} diff --git a/modules.d/01systemd-timesyncd/timesyncd-tmpfile-dracut.conf b/modules.d/01systemd-timesyncd/timesyncd-tmpfile-dracut.conf new file mode 100644 index 0000000..14c78bb --- /dev/null +++ b/modules.d/01systemd-timesyncd/timesyncd-tmpfile-dracut.conf @@ -0,0 +1,2 @@ +# This file is part of dracut systemd-timesyncd module. +d /run/systemd/netif/links 0755 root root - diff --git a/modules.d/01systemd-tmpfiles/module-setup.sh b/modules.d/01systemd-tmpfiles/module-setup.sh new file mode 100755 index 0000000..6bd42c0 --- /dev/null +++ b/modules.d/01systemd-tmpfiles/module-setup.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries systemd-tmpfiles || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + # Excluding "$tmpfilesdir/home.conf", sets up /home /srv + # Excluding "$tmpfilesdir/journal-nocow.conf", requires specific btrfs setup + # Excluding "$tmpfilesdir/legacy.conf", belongs in separated legacy module + # Excluding "$tmpfilesdir/systemd-nologin.conf", belongs in separated pam module + # Excluding "$tmpfilesdir/systemd-nspawn.conf", belongs in separated machined module + # Excluding "$tmpfilesdir/x11.conf", belongs in separated x11 module + + inst_multiple -o \ + /usr/lib/group \ + /usr/lib/passwd \ + "$tmpfilesdir/etc.conf" \ + "$tmpfilesdir/static-nodes-permissions.conf" \ + "$tmpfilesdir/systemd-tmp.conf" \ + "$tmpfilesdir/systemd.conf" \ + "$tmpfilesdir/var.conf" \ + "$systemdsystemunitdir"/systemd-tmpfiles-clean.service \ + "$systemdsystemunitdir/systemd-tmpfiles-clean.service.d/*.conf" \ + "$systemdsystemunitdir"/systemd-tmpfiles-setup.service \ + "$systemdsystemunitdir/systemd-tmpfiles-setup.service.d/*.conf" \ + "$systemdsystemunitdir"/systemd-tmpfiles-setup-dev.service \ + "$systemdsystemunitdir/systemd-tmpfiles-setup-dev.service.d/*.conf" \ + "$systemdsystemunitdir"/systemd-tmpfiles-setup-dev-early.service \ + "$systemdsystemunitdir/systemd-tmpfiles-setup-dev-early.service.d/*.conf" \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-tmpfiles-setup.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-tmpfiles-setup-dev.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-tmpfiles-setup-dev-early.service \ + systemd-tmpfiles + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + /etc/group \ + /etc/passwd \ + "$tmpfilesconfdir/*.conf" \ + "$systemdsystemconfdir"/systemd-tmpfiles-clean.service \ + "$systemdsystemconfdir/systemd-tmpfiles-clean.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-tmpfiles-setup.service \ + "$systemdsystemconfdir/systemd-tmpfiles-setup.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-tmpfiles-setup-dev.service \ + "$systemdsystemconfdir/systemd-tmpfiles-setup-dev.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-tmpfiles-setup-dev-early.service \ + "$systemdsystemconfdir/systemd-tmpfiles-setup-dev-early.service.d/*.conf" + fi + +} diff --git a/modules.d/01systemd-udevd/module-setup.sh b/modules.d/01systemd-udevd/module-setup.sh new file mode 100755 index 0000000..ce5d2a3 --- /dev/null +++ b/modules.d/01systemd-udevd/module-setup.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries \ + udevadm \ + "$systemdutildir"/systemd-udevd \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "$udevdir"/hwdb.bin \ + "$udevdir"/udev.conf \ + "$udevdir"/ata_id \ + "$udevdir"/cdrom_id \ + "$udevdir"/dmi_memory_id \ + "$udevdir"/fido_id \ + "$udevdir"/mtd_probe \ + "$udevdir"/mtp-probe \ + "$udevdir"/scsi_id \ + "$udevdir"/v4l_id \ + "$udevrulesdir"/50-udev-default.rules \ + "$udevrulesdir"/60-autosuspend.rules \ + "$udevrulesdir"/60-block.rules \ + "$udevrulesdir"/60-cdrom_id.rules \ + "$udevrulesdir"/60-drm.rules \ + "$udevrulesdir"/60-evdev.rules \ + "$udevrulesdir"/60-fido-id.rules \ + "$udevrulesdir"/60-input-id.rules \ + "$udevrulesdir"/60-persistent-alsa.rules \ + "$udevrulesdir"/60-persistent-input.rules \ + "$udevrulesdir"/60-persistent-storage-tape.rules \ + "$udevrulesdir"/60-persistent-storage.rules \ + "$udevrulesdir"/60-persistent-v4l.rules \ + "$udevrulesdir"/60-sensor.rules \ + "$udevrulesdir"/60-serial.rules \ + "$udevrulesdir"/64-btrfs.rules \ + "$udevrulesdir"/70-joystick.rules \ + "$udevrulesdir"/70-memory.rules \ + "$udevrulesdir"/70-mouse.rules \ + "$udevrulesdir"/70-touchpad.rules \ + "$udevrulesdir"/75-net-description.rules \ + "$udevrulesdir"/75-probe_mtd.rules \ + "$udevrulesdir"/78-sound-card.rules \ + "$udevrulesdir"/80-drivers.rules \ + "$udevrulesdir"/80-net-setup-link.rules \ + "$udevrulesdir"/81-net-dhcp.rules \ + "$udevrulesdir"/99-systemd.rules \ + "$systemdutildir"/systemd-udevd \ + "$systemdsystemunitdir"/systemd-udevd.service \ + "$systemdsystemunitdir/systemd-udevd.service.d/*.conf" \ + "$systemdsystemunitdir"/systemd-udev-trigger.service \ + "$systemdsystemunitdir/systemd-udev-trigger.service.d/*.conf" \ + "$systemdsystemunitdir"/systemd-udev-settle.service \ + "$systemdsystemunitdir/systemd-udev-settle.service.d/*.conf" \ + "$systemdsystemunitdir"/systemd-udevd-control.socket \ + "$systemdsystemunitdir"/systemd-udevd-kernel.socket \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-udevd-control.socket \ + "$systemdsystemunitdir"/sockets.target.wants/systemd-udevd-kernel.socket \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-udevd.service \ + "$systemdsystemunitdir"/sysinit.target.wants/systemd-udev-trigger.service \ + udevadm + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$udevconfdir"/hwdb.bin \ + "$udevconfdir"/udev.conf \ + "$udevrulesconfdir/*.rules" \ + "$systemdutilconfdir"/hwdb/hwdb.bin \ + "$systemdsystemconfdir"/systemd-udevd.service \ + "$systemdsystemconfdir/systemd-udevd.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-udev-trigger.service \ + "$systemdsystemconfdir/systemd-udev-trigger.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-udev-settle.service \ + "$systemdsystemconfdir/systemd-udev-settle.service.d/*.conf" \ + "$systemdsystemconfdir"/systemd-udevd-control.socket \ + "$systemdsystemconfdir/systemd-udevd-control.socket.d/*.conf" \ + "$systemdsystemconfdir"/systemd-udevd-kernel.socket \ + "$systemdsystemconfdir/systemd-udevd-kernel.socket.d/*.conf" \ + "$systemdsystemconfdir"/sockets.target.wants/systemd-udevd-control.socket \ + "$systemdsystemconfdir"/sockets.target.wants/systemd-udevd-kernel.socket \ + "$systemdsystemconfdir"/sysinit.target.wants/systemd-udevd.service \ + "$systemdsystemconfdir"/sysinit.target.wants/systemd-udev-trigger.service + fi + + # Install required libraries. + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file {"tls/$_arch/",tls/,"$_arch/",}"libudev.so.*" + +} diff --git a/modules.d/01systemd-veritysetup/module-setup.sh b/modules.d/01systemd-veritysetup/module-setup.sh new file mode 100755 index 0000000..fecfecc --- /dev/null +++ b/modules.d/01systemd-veritysetup/module-setup.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries \ + "$systemdutildir"/systemd-veritysetup \ + "$systemdutildir"/system-generators/systemd-veritysetup-generator \ + || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd dm + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "$systemdutildir"/systemd-veritysetup \ + "$systemdutildir"/system-generators/systemd-veritysetup-generator \ + "$systemdsystemunitdir"/remote-veritysetup.target \ + "$systemdsystemunitdir"/veritysetup-pre.target \ + "$systemdsystemunitdir"/veritysetup.target \ + "$systemdsystemunitdir"/sysinit.target.wants/veritysetup.target \ + "$systemdsystemunitdir"/initrd-root-device.target.wants/remote-veritysetup.target + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + /etc/veritytab \ + "$systemdsystemconfdir"/veritysetup.target \ + "$systemdsystemconfdir/veritysetup.target.wants/*.target" \ + "$systemdsystemconfdir"/veritysetup-pre.target \ + "$systemdsystemconfdir/veritysetup-pre.target.wants/*.target" \ + "$systemdsystemconfdir"/remote-veritysetup.target \ + "$systemdsystemconfdir/remote-veritysetup.target.wants/*.target" \ + "$systemdsystemconfdir"/sysinit.target.wants/veritysetup.target \ + "$systemdsystemconfdir/sysinit.target.wants/veritysetup.target.wants/*.target" \ + "$systemdsystemconfdir"/initrd-root-device.target.wants/remote-veritysetup.target + fi + + # Install required libraries. + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file {"tls/$_arch/",tls/,"$_arch/",}"libcryptsetup.so.*" + +} diff --git a/modules.d/02caps/README b/modules.d/02caps/README new file mode 100644 index 0000000..34e0f02 --- /dev/null +++ b/modules.d/02caps/README @@ -0,0 +1,33 @@ +This adds the following parameters: +rd.caps=1 + turn the caps module on/off +rd.caps.initdrop=cap_sys_module,cap_sys_rawio + drop the specified comma separated capabilities +rd.caps.disablemodules=1 + turn off module loading +rd.caps.disablekexec=1 + turn off the kexec functionality + +If module loading is turned off, all modules have to be loaded in the +initramfs, which are used later on. This can be done with +"rd.driver.pre=" +rd.driver.pre=autofs4,sunrpc,ipt_REJECT,nf_conntrack_ipv4,.... + +Because the kernel command line would get huge with all those drivers, I +recommend to make use of $initramfs/etc/cmdline. + +So, all rd.caps.* and rd.driver.pre arguments are in caps.conf can be +copied to $initramfs/etc/cmdline with "-i caps.conf /etc/cmdline". + +Also all modules have to be loaded in the initramfs via "--add-drivers". + +The resulting initramfs creation would look like this: + + --add-drivers "autofs4 sunrpc ipt_REJECT nf_conntrack_ipv4 \ + nf_defrag_ipv4 iptable_filter ip_tables + ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack + ip6table_filter ip6_tables dm_mirror dm_region_hash dm_log uinput ppdev + parport_pc parport ipv6 sg 8139too 8139cp mii i2c_piix4 i2c_core ext3 + jbd mbcache sd_mod crc_t10dif sr_mod cdrom ata_generic pata_acpi ata_piix + dm_mod" \ + /boot/initramfs-caps.img diff --git a/modules.d/02caps/caps.sh b/modules.d/02caps/caps.sh new file mode 100755 index 0000000..6c28299 --- /dev/null +++ b/modules.d/02caps/caps.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +capsmode=$(getarg rd.caps) + +if [ "$capsmode" = "1" ]; then + CAPS_INIT_DROP=$(getarg rd.caps.initdrop=) + # shellcheck disable=SC2016 + CAPS_USERMODEHELPER_BSET=$(capsh --drop="$CAPS_INIT_DROP" -- -c 'while read a b || [ -n "$a" ]; do [ "$a" = "CapBnd:" ] && echo $((0x${b:$((${#b}-8)):8})) $((0x${b:$((${#b}-16)):8})) && break; done < /proc/self/status') + CAPS_MODULES_DISABLED=$(getarg rd.caps.disablemodules=) + CAPS_KEXEC_DISABLED=$(getarg rd.caps.disablekexec=) + + info "Loading CAPS_MODULES $CAPS_MODULES" + for i in $CAPS_MODULES; do modprobe "$i" 2>&1 > /dev/null | vinfo; done + + if [ "$CAPS_MODULES_DISABLED" = "1" -a -e /proc/sys/kernel/modules_disabled ]; then + info "Disabling module loading." + echo "$CAPS_MODULES_DISABLED" > /proc/sys/kernel/modules_disabled + fi + + if [ "$CAPS_KEXEC_DISABLED" = "1" -a -e /proc/sys/kernel/kexec_disabled ]; then + info "Disabling kexec." + echo "$CAPS_KEXEC_DISABLED" > /proc/sys/kernel/kexec_disabled + fi + + info "CAPS_USERMODEHELPER_BSET=$CAPS_USERMODEHELPER_BSET" + if [ -e /proc/sys/kernel/usermodehelper/bset ]; then + info "Setting usermode helper bounding set." + echo "$CAPS_USERMODEHELPER_BSET" > /proc/sys/kernel/usermodehelper/bset + echo "$CAPS_USERMODEHELPER_BSET" > /proc/sys/kernel/usermodehelper/inheritable + fi + + echo "CAPS_INIT_DROP=\"$CAPS_INIT_DROP\"" > /etc/capsdrop + info "Will drop capabilities $CAPS_INIT_DROP from init." +fi diff --git a/modules.d/02caps/module-setup.sh b/modules.d/02caps/module-setup.sh new file mode 100755 index 0000000..18073e2 --- /dev/null +++ b/modules.d/02caps/module-setup.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# called by dracut +check() { + require_binaries capsh + return 255 +} + +# called by dracut +depends() { + echo bash + return 0 +} + +# called by dracut +install() { + if ! dracut_module_included "systemd"; then + inst_hook pre-pivot 00 "$moddir/caps.sh" + inst "$(find_binary capsh 2> /dev/null)" /usr/sbin/capsh + # capsh wants bash and we need bash also + inst /bin/bash + else + dwarning "caps: does not work with systemd in the initramfs" + fi +} diff --git a/modules.d/03modsign/load-modsign-keys.sh b/modules.d/03modsign/load-modsign-keys.sh new file mode 100755 index 0000000..a489067 --- /dev/null +++ b/modules.d/03modsign/load-modsign-keys.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Licensed under the GPLv2 +# +# Copyright 2013 Red Hat, Inc. +# Peter Jones <pjones@redhat.com> + +for x in /lib/modules/keys/*; do + [ "${x}" = "/lib/modules/keys/*" ] && break + keyctl padd asymmetric "" %:.secondary_trusted_keys < "${x}" +done diff --git a/modules.d/03modsign/module-setup.sh b/modules.d/03modsign/module-setup.sh new file mode 100755 index 0000000..56e2bdb --- /dev/null +++ b/modules.d/03modsign/module-setup.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# +# Licensed under the GPLv2 +# +# Copyright 2013 Red Hat, Inc. +# Peter Jones <pjones@redhat.com> + +# called by dracut +check() { + require_binaries keyctl || return 1 + + # do not include module in hostonly mode, + # if no keys are present + if [[ $hostonly ]]; then + x=$(echo "$dracutsysrootdir"/lib/modules/keys/*) + [[ ${x} == "$dracutsysrootdir/lib/modules/keys/*" ]] && return 255 + fi + + return 0 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_dir /lib/modules/keys + inst_binary keyctl + + inst_hook pre-trigger 01 "$moddir/load-modsign-keys.sh" + + for x in "$dracutsysrootdir"/lib/modules/keys/*; do + [[ ${x} == "$dracutsysrootdir/lib/modules/keys/*" ]] && break + inst_simple "${x#"$dracutsysrootdir"}" + done +} diff --git a/modules.d/03rescue/module-setup.sh b/modules.d/03rescue/module-setup.sh new file mode 100755 index 0000000..d7e7e6f --- /dev/null +++ b/modules.d/03rescue/module-setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# called by dracut +check() { + # do not add this module by default + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_multiple -o ps grep more cat rm strace free showmount \ + ping netstat rpcinfo vi scp ping6 ssh \ + fsck fsck.ext2 fsck.ext4 fsck.ext3 fsck.ext4dev fsck.f2fs fsck.vfat e2fsck +} diff --git a/modules.d/04watchdog-modules/module-setup.sh b/modules.d/04watchdog-modules/module-setup.sh new file mode 100755 index 0000000..2a94e34 --- /dev/null +++ b/modules.d/04watchdog-modules/module-setup.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + return 0 +} + +installkernel() { + local -A _drivers + local _wdtdrv + + for _wd in /sys/class/watchdog/*; do + ! [ -e "$_wd" ] && continue + _wdtdrv=$(get_dev_module "$_wd") + if [[ $_wdtdrv ]]; then + instmods "$_wdtdrv" + for i in $_wdtdrv; do + _drivers[$i]=1 + done + fi + done + + # ensure that watchdog module is loaded as early as possible + if [[ ${!_drivers[*]} ]]; then + echo "rd.driver.pre=\"$( + IFS=, + echo "${!_drivers[*]}" + )\"" > "${initdir}"/etc/cmdline.d/00-watchdog.conf + fi + return 0 +} diff --git a/modules.d/04watchdog/module-setup.sh b/modules.d/04watchdog/module-setup.sh new file mode 100755 index 0000000..e9ce878 --- /dev/null +++ b/modules.d/04watchdog/module-setup.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + echo watchdog-modules + return 0 +} + +# called by dracut +install() { + # Do not add watchdog hooks if systemd module is included + # In that case, systemd will manage watchdog kick + if ! dracut_module_included "systemd"; then + inst_hook cmdline 00 "$moddir/watchdog.sh" + inst_hook cmdline 50 "$moddir/watchdog.sh" + inst_hook pre-trigger 00 "$moddir/watchdog.sh" + inst_hook initqueue 00 "$moddir/watchdog.sh" + inst_hook mount 00 "$moddir/watchdog.sh" + inst_hook mount 50 "$moddir/watchdog.sh" + inst_hook mount 99 "$moddir/watchdog.sh" + inst_hook pre-pivot 00 "$moddir/watchdog.sh" + inst_hook pre-pivot 99 "$moddir/watchdog.sh" + inst_hook cleanup 00 "$moddir/watchdog.sh" + inst_hook cleanup 99 "$moddir/watchdog.sh" + fi + + inst_hook emergency 02 "$moddir/watchdog-stop.sh" + inst_multiple -o wdctl +} diff --git a/modules.d/04watchdog/watchdog-stop.sh b/modules.d/04watchdog/watchdog-stop.sh new file mode 100755 index 0000000..921f969 --- /dev/null +++ b/modules.d/04watchdog/watchdog-stop.sh @@ -0,0 +1,2 @@ +#!/bin/sh +[ -c /dev/watchdog ] && printf 'V' > /dev/watchdog diff --git a/modules.d/04watchdog/watchdog.sh b/modules.d/04watchdog/watchdog.sh new file mode 100755 index 0000000..22919ee --- /dev/null +++ b/modules.d/04watchdog/watchdog.sh @@ -0,0 +1,12 @@ +#!/bin/sh +if [ -e /dev/watchdog ]; then + if [ ! -e /tmp/watchdog_timeout ]; then + wdctl -s 60 /dev/watchdog > /dev/null 2>&1 + : > /tmp/watchdog_timeout + fi + info "Triggering watchdog" + : > /dev/watchdog +else + modprobe ib700wdt + modprobe i6300esb +fi diff --git a/modules.d/05busybox/module-setup.sh b/modules.d/05busybox/module-setup.sh new file mode 100755 index 0000000..86b3761 --- /dev/null +++ b/modules.d/05busybox/module-setup.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# called by dracut +check() { + require_binaries busybox || return 1 + + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + local _i _path _busybox + local _progs=() + _busybox=$(find_binary busybox) + inst "$_busybox" /usr/bin/busybox + for _i in $($_busybox --list); do + [[ ${_i} == busybox ]] && continue + _progs+=("${_i}") + done + + for _i in "${_progs[@]}"; do + _path=$(find_binary "$_i") + [ -z "$_path" ] && continue + ln_r /usr/bin/busybox "$_path" + done +} diff --git a/modules.d/06dbus-broker/module-setup.sh b/modules.d/06dbus-broker/module-setup.sh new file mode 100755 index 0000000..a38fce8 --- /dev/null +++ b/modules.d/06dbus-broker/module-setup.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries busctl || return 1 + require_binaries dbus-broker || return 1 + require_binaries dbus-broker-launch || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + # This module has external dependency on the systemd module. + echo systemd systemd-sysusers + # Return 0 to include the dependent systemd module in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + # Create dbus related directories. + inst_dir "$dbus" + inst_dir "$dbusinterfaces" + inst_dir "$dbusservices" + inst_dir "$dbussession" + inst_dir "$dbussystem" + inst_dir "$dbussystemservices" + inst_dir "$dbusconfdir" + inst_dir "$dbusinterfacesconfdir" + inst_dir "$dbusservicesconfdir" + inst_dir "$dbussessionconfdir" + inst_dir "$dbussystemconfdir" + inst_dir "$dbussystemservicesconfdir" + + inst_multiple -o \ + "$dbus"/session.conf \ + "$dbus"/system.conf \ + "$dbussystem"/org.freedesktop.systemd1.conf \ + "$dbusservicesconfdir"/org.freedesktop.systemd1.service \ + "$dbussystemservices"/org.freedesktop.systemd1.service \ + "$sysusers"/dbus.conf \ + "$systemdcatalog"/dbus-broker.catalog \ + "$systemdcatalog"/dbus-broker-launch.catalog \ + "$systemdsystemunitdir"/dbus-broker.service \ + "$systemduser"/dbus-broker.service \ + "$systemdsystemunitdir"/dbus.socket \ + "$systemduser"/dbus.socket \ + "$systemdsystemunitdir"/sockets.target.wants/dbus.socket \ + "$systemduser"/sockets.target.wants/dbus.socket \ + "$systemdsystemunitdir"/dbus.target.wants \ + busctl dbus-broker dbus-broker-launch + + # Adjusting dependencies for initramfs in the dbus socket unit. + # shellcheck disable=SC1004 + sed -i -e \ + '/^\[Unit\]/aDefaultDependencies=no\ + Conflicts=shutdown.target\ + Before=shutdown.target + /^\[Socket\]/aRemoveOnStop=yes' \ + "$initdir$systemdsystemunitdir/dbus.socket" + + $SYSTEMCTL -q --root "$initdir" enable dbus-broker.service + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$dbusconfdir"/session.conf \ + "$dbusconfdir"/system.conf \ + "$sysusersconfdir"/dbus.conf \ + "$systemdsystemconfdir"/dbus.socket \ + "$systemdsystemconfdir"/dbus.socket.d/*.conf \ + "$systemdsystemconfdir"/dbus-broker.service \ + "$systemdsystemconfdir"/dbus-broker.service.d/*.conf \ + "$systemdsystemconfdir"/sockets.target.wants/dbus.socket + fi + + # We need to make sure that systemd-tmpfiles-setup.service->dbus.socket + # will not wait for local-fs.target to start if swap is encrypted, + # this would make dbus wait the timeout for the swap before loading. + # This could delay sysinit services that are dependent on dbus.service. + sed -i -Ee \ + '/^After/s/(After[[:space:]]*=.*)(local-fs.target[[:space:]]*)(.*)/\1-\.mount \3/' \ + "$initdir$systemdsystemunitdir/systemd-tmpfiles-setup.service" +} diff --git a/modules.d/06dbus-daemon/module-setup.sh b/modules.d/06dbus-daemon/module-setup.sh new file mode 100755 index 0000000..8345585 --- /dev/null +++ b/modules.d/06dbus-daemon/module-setup.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed + require_binaries busctl || return 1 + require_binaries dbus-daemon || return 1 + require_binaries dbus-send || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on the systemd module. + echo systemd + # Return 0 to include the dependent systemd module in the initramfs. + return 0 +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + # dbus conflicts with dbus-broker. + if dracut_module_included "dbus-broker"; then + derror "dbus conflicts with dbus-broker in the initramfs." + return 1 + fi + + # Create dbus related directories. + inst_dir "$dbus" + inst_dir "$dbusinterfaces" + inst_dir "$dbusservices" + inst_dir "$dbussession" + inst_dir "$dbussystem" + inst_dir "$dbussystemservices" + inst_dir "$dbusconfdir" + inst_dir "$dbusinterfacesconfdir" + inst_dir "$dbusservicesconfdir" + inst_dir "$dbussessionconfdir" + inst_dir "$dbussystemconfdir" + inst_dir "$dbussystemservicesconfdir" + + inst_multiple -o \ + "$dbus"/system.conf \ + "$dbussystem"/org.freedesktop.systemd1.conf \ + "$dbusservicesconfdir"/org.freedesktop.systemd1.service \ + "$dbussystemservices"/org.freedesktop.systemd1.service \ + "$systemdsystemunitdir"/dbus.service \ + "$systemdsystemunitdir"/dbus.socket \ + "$systemdsystemunitdir"/dbus.target.wants \ + busctl dbus-send dbus-daemon + + # Adjusting dependencies for initramfs in the dbus service unit. + # shellcheck disable=SC1004 + sed -i -e \ + '/^\[Unit\]/aDefaultDependencies=no\ + Conflicts=shutdown.target\ + Before=shutdown.target' \ + "$initdir$systemdsystemunitdir/dbus.service" + + # Adjusting dependencies for initramfs in the dbus socket unit. + # shellcheck disable=SC1004 + sed -i -e \ + '/^\[Unit\]/aDefaultDependencies=no\ + Conflicts=shutdown.target\ + Before=shutdown.target + /^\[Socket\]/aRemoveOnStop=yes' \ + "$initdir$systemdsystemunitdir/dbus.socket" + + # Adding the user and group for dbus + grep '^\(d\|message\)bus:' "$dracutsysrootdir"/etc/passwd >> "$initdir/etc/passwd" + grep '^\(d\|message\)bus:' "$dracutsysrootdir"/etc/group >> "$initdir/etc/group" + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + "$dbusconfdir"/system.conf \ + "$systemdsystemconfdir"/dbus.socket \ + "$systemdsystemconfdir"/dbus.socket.d/*.conf \ + "$systemdsystemconfdir"/dbus.service \ + "$systemdsystemconfdir"/dbus.service.d/*.conf + fi + + # We need to make sure that systemd-tmpfiles-setup.service->dbus.socket + # will not wait for local-fs.target to start if swap is encrypted, + # this would make dbus wait the timeout for the swap before loading. + # This could delay sysinit services that are dependent on dbus.service. + sed -i -Ee \ + '/^After/s/(After[[:space:]]*=.*)(local-fs.target[[:space:]]*)(.*)/\1-\.mount \3/' \ + "$initdir$systemdsystemunitdir/systemd-tmpfiles-setup.service" +} diff --git a/modules.d/06rngd/module-setup.sh b/modules.d/06rngd/module-setup.sh new file mode 100755 index 0000000..aec8d57 --- /dev/null +++ b/modules.d/06rngd/module-setup.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: +# +# Copyright (c) 2019 Red Hat, Inc. +# Author: Renaud Métrich <rmetrich@redhat.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +check() { + # if there's no rngd binary, no go. + require_binaries rngd || return 1 + + return 0 +} + +depends() { + echo systemd + return 0 +} + +install() { + inst rngd + inst_simple "${moddir}/rngd.service" "${systemdsystemunitdir}/rngd.service" + # make sure dependent libs are installed too + inst_libdir_file opensc-pkcs11.so + + $SYSTEMCTL -q --root "$initdir" add-wants sysinit.target rngd.service +} diff --git a/modules.d/06rngd/rngd.service b/modules.d/06rngd/rngd.service new file mode 100644 index 0000000..dd5374d --- /dev/null +++ b/modules.d/06rngd/rngd.service @@ -0,0 +1,8 @@ +[Unit] +Description=Hardware RNG Entropy Gatherer Daemon +DefaultDependencies=no +Before=systemd-udevd.service +ConditionVirtualization=!container + +[Service] +ExecStart=/usr/sbin/rngd -f diff --git a/modules.d/09dbus/module-setup.sh b/modules.d/09dbus/module-setup.sh new file mode 100755 index 0000000..679483d --- /dev/null +++ b/modules.d/09dbus/module-setup.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + # We only want to return 255 since this is a meta module. + return 255 +} + +# Module dependency requirements. +depends() { + local _module + # Add a dbus meta dependency based on the module in use. + for _module in dbus-daemon dbus-broker; do + if dracut_module_included "$_module"; then + echo "$_module" + return 0 + fi + done + + if check_module "dbus-broker"; then + echo "dbus-broker" + return 0 + else + echo "dbus-daemon" + return 0 + fi + + return 1 +} diff --git a/modules.d/10i18n/10-console.rules b/modules.d/10i18n/10-console.rules new file mode 100644 index 0000000..640b1db --- /dev/null +++ b/modules.d/10i18n/10-console.rules @@ -0,0 +1,2 @@ +# Console initialization - keyboard, font, etc. +KERNEL=="tty0", RUN+="/sbin/initqueue --onetime --unique --name console_init_$name /lib/udev/console_init $root/$name" diff --git a/modules.d/10i18n/README b/modules.d/10i18n/README new file mode 100644 index 0000000..6cbbae9 --- /dev/null +++ b/modules.d/10i18n/README @@ -0,0 +1,124 @@ +dracut i18n module +------------------ + +INDEX + +0. Introduction +1. Hostonly vs Generic +2. Configuration + 2.1. Variables + 2.2. Setting up mappings + 2.3. Additional settings +3. Kernel parameters + +~ + +0. Introduction + +i18n module provides internationalization for initramfs at runtime. It +is intended to be generic across different GNU/Linux distributions. + +i18n and keyboard settings are stored in different files among +distributions. To deal with it avoiding hardcoding those differences in +the installation script we handle it by mappings between variables used +by dracut and the ones in the system. Package maintainer is expected to +create those for his/her distribution and it's appreciated to share it +with us, so we can include it in source package. + + +1. Hostonly vs Generic + +If you invoke dracut with '-H' option, i18n module install script will +gather variables values from your configuration files using mappings +provided in "/etc/dracut.conf.d/<foo>.conf". Those variables will be +put in "etc/vconsole.conf" and "etc/locale.conf" files inside initramfs +image. Next it will install only declared font, keymaps and so. + +When building generic image (dracut without '-H' option), install script +copies all content of directories: consolefonts, consoletrans, unimaps +and keymaps to image. Take into account that's about 4 MiB. + + +2. Configuration + +2.1. Variables + +The following variables are used by i18n install script and at initramfs +runtime: + + KEYMAP - keyboard translation table loaded by loadkeys + KEYTABLE - base name for keyboard translation table; if UNICODE is + true, Unicode version will be loaded. Overrides KEYMAP. + EXT_KEYMAPS - list of extra keymaps to bo loaded (sep. by space) + UNICODE - boolean, indicating UTF-8 mode + FONT - console font + FONT_MAP - see description of '-m' parameter in setfont manual + FONT_UNIMAP - see description of '-u' parameter in setfont manual + +The following are appended to EXT_KEYMAPS only during build time: + + UNIKEYMAP + GRP_TOGGLE + +They were used in 10redhat-i18n module, but not sure of its purpose. +I'm leaving it in case... The following are taken from the environment: + + LANG + LC_ALL + +If UNICODE variable is not provided, script indicates if UTF-8 should be +used on the basis of LANG value (if it ends with ".utf8" or similar). + + +2.2. Setting up mappings + +Mappings between variables listed in 2.1. and the ones spread around +your system are set up in /etc/dracut.conf.d/<foo>.conf. You need to +assign mappings to i18n_vars. Here's an example: + +i18n_vars="/etc/conf.d/keymaps:KEYMAP,EXTENDED_KEYMAPS-EXT_KEYMAPS /etc/conf.d/consolefont:CONSOLEFONT-FONT,CONSOLETRANSLATION-FONT_MAP /etc/rc.conf:UNICODE" + +First we've got name of file in host file system tree. After colon +there's mapping: <from>-<to>. If both variables have the same name you +can enter just a single, but it's important to specify it! The module +will source only variables you've listed. + +Below there's detailed description in BNF: + +<list> ::= <element> | <element> " " <list> +<element> ::= <conf-file-name> ":" <map-list> +<map-list> ::= <mapping> | <mapping> "," <map-list> +<mapping> ::= <src-var> "-" <dst-var> | <src-var> + +We assume no whitespace are allowed between symbols. +<conf-file-name> is a file holding <src-var> in your system. +<src-var> is a variable holding value of meaning the same as <dst-var>. +<dst-var> is a variable which will be set up inside initramfs. +If <dst-var> has the same name as <src-var> we can omit <dst-var>. + +Example: +/etc/conf.d/keymaps:KEYMAP,extended_keymaps-EXT_KEYMAPS +<list> = /etc/conf.d/keymaps:KEYMAP,extended_keymaps-EXT_KEYMAPS +<element> = /etc/conf.d/keymaps:KEYMAP,extended_keymaps-EXT_KEYMAPS +<conf-file-name> = /etc/conf.d/keymaps +<map-list> = KEYMAP,extended_keymaps-EXT_KEYMAPS +<mapping> = KEYMAP +<src-var> = KEYMAP +<mapping> = extended_keymaps-EXT_KEYMAPS +<src-var> = extended_keymaps +<dst-var> = EXT_KEYMAPS + + +2.3. Additional settings + +If you encounter following error message: "Directories consolefonts, +consoletrans, keymaps, unimaps not found.", you can provide path where +those directories lie in your system by setting kbddir in configuration +file (the same where you put mappings). + + +3. Kernel parameters + +If you create generic initramfs you can set up i18n by kernel +parameters using variables listed in 2.1. (except of UNIKEYMAP +and GRP_TOGGLE) The recommended minimum is: FONT and KEYMAP. diff --git a/modules.d/10i18n/console_init.sh b/modules.d/10i18n/console_init.sh new file mode 100755 index 0000000..3ca0ac1 --- /dev/null +++ b/modules.d/10i18n/console_init.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +[ -n "$DRACUT_SYSTEMD" ] && exit 0 + +if [ -x "$systemdutildir"/systemd-vconsole-setup ]; then + "$systemdutildir"/systemd-vconsole-setup "$@" +fi + +[ -e /etc/vconsole.conf ] && . /etc/vconsole.conf + +DEFAULT_FONT=eurlatgr +DEFAULT_KEYMAP=/etc/sysconfig/console/default.kmap + +set_keyboard() { + local param + + [ "${UNICODE}" = 1 ] && param=-u || param=-a + kbd_mode ${param} +} + +set_terminal() { + local dev="$1" + + if [ "${UNICODE}" = 1 ]; then + printf '\033%%G' >&7 + stty -F "${dev}" iutf8 + else + printf '\033%%@' >&7 + stty -F "${dev}" -iutf8 + fi +} + +set_keymap() { + local utf_switch + + if [ -z "${KEYMAP}" ]; then + [ -f "${DEFAULT_KEYMAP}" ] && KEYMAP=${DEFAULT_KEYMAP} + fi + + [ -n "${KEYMAP}" ] || return 1 + + [ "${UNICODE}" = 1 ] && utf_switch=-u + + # shellcheck disable=SC2086 + loadkeys -q ${utf_switch} ${KEYMAP} ${EXT_KEYMAPS} +} + +set_font() { + setfont "${FONT-${DEFAULT_FONT}}" \ + -C "${1}" \ + ${FONT_MAP:+-m "${FONT_MAP}"} \ + ${FONT_UNIMAP:+-u "${FONT_UNIMAP}"} +} + +dev_close() { + exec 6>&- + exec 7>&- +} + +dev_open() { + local dev="$1" + + exec 6< "${dev}" \ + && exec 7>> "${dev}" +} + +dev=/dev/${1#/dev/} + +[ -c "${dev}" ] || { + echo "Usage: $0 device" >&2 + exit 1 +} + +dev_open "${dev}" + +for fd in 6 7; do + if ! [ -t ${fd} ]; then + echo "ERROR: File descriptor not opened: ${fd}" >&2 + dev_close + exit 1 + fi +done + +set_keyboard +set_terminal "${dev}" +set_font "${dev}" +set_keymap + +dev_close diff --git a/modules.d/10i18n/module-setup.sh b/modules.d/10i18n/module-setup.sh new file mode 100755 index 0000000..ac45611 --- /dev/null +++ b/modules.d/10i18n/module-setup.sh @@ -0,0 +1,297 @@ +#!/bin/bash + +# called by dracut +check() { + [[ "$mount_needs" ]] && return 1 + + require_binaries setfont loadkeys kbd_mode || return 1 + + return 0 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + declare -A KEYMAPS + + if dracut_module_included "systemd"; then + unset FONT + unset KEYMAP + # shellcheck disable=SC1090 + [[ -f "$dracutsysrootdir"/etc/vconsole.conf ]] && . "$dracutsysrootdir"/etc/vconsole.conf + fi + + KBDSUBDIRS=(consolefonts consoletrans keymaps unimaps) + DEFAULT_FONT="${i18n_default_font:-eurlatgr}" + I18N_CONF="/etc/locale.conf" + VCONFIG_CONF="/etc/vconsole.conf" + + findkeymap() { + local -a MAPS + local MAPNAME + local INCLUDES + local MAP + local CMD + local FN + + if [[ -f $dracutsysrootdir$1 ]]; then + MAPS=("$1") + else + MAPNAME=${1%.map*} + + mapfile -t -d '' MAPS < <( + find "${dracutsysrootdir}${kbddir}"/keymaps/ -type f \( -name "${MAPNAME}" -o -name "${MAPNAME}.map*" \) -print0 + ) + fi + + for MAP in "${MAPS[@]}"; do + [[ -f $MAP ]] || continue + [[ -v KEYMAPS["$MAP"] ]] && continue + + KEYMAPS["$MAP"]=1 + + case "$MAP" in + *.gz) CMD="zgrep" ;; + *.bz2) CMD="bzgrep" ;; + *) CMD="grep" ;; + esac + + readarray -t INCLUDES < <("$CMD" '^include ' "$MAP" | while read -r _ a _ || [ -n "$a" ]; do echo "${a//\"/}"; done) + + for INCL in "${INCLUDES[@]}"; do + local -a FNS + mapfile -t -d '' FNS < <(find "${dracutsysrootdir}${kbddir}"/keymaps/ -type f -name "${INCL}*" -print0) + for FN in "${FNS[@]}"; do + [[ -f $FN ]] || continue + [[ -v KEYMAPS["$FN"] ]] || findkeymap "$FN" + done + done + done + } + + # Function gathers variables from distributed files among the tree, maps to + # specified names and prints the result in format "new-name=value". + # + # $@ = list in format specified below (BNF notation) + # + # <list> ::= <element> | <element> " " <list> + # <element> ::= <conf-file-name> ":" <map-list> + # <map-list> ::= <mapping> | <mapping> "," <map-list> + # <mapping> ::= <src-var> "-" <dst-var> | <src-var> + # + # We assume no whitespace are allowed between symbols. + # <conf-file-name> is a file holding <src-var> in your system. + # <src-var> is a variable holding value of meaning the same as <dst-var>. + # <dst-var> is a variable which will be set up inside initramfs. + # If <dst-var> has the same name as <src-var> we can omit <dst-var>. + # + # Example: + # /etc/conf.d/keymaps:KEYMAP,extended_keymaps-EXT_KEYMAPS + # <list> = /etc/conf.d/keymaps:KEYMAP,extended_keymaps-EXT_KEYMAPS + # <element> = /etc/conf.d/keymaps:KEYMAP,extended_keymaps-EXT_KEYMAPS + # <conf-file-name> = /etc/conf.d/keymaps + # <map-list> = KEYMAP,extended_keymaps-EXT_KEYMAPS + # <mapping> = KEYMAP + # <src-var> = KEYMAP + # <mapping> = extended_keymaps-EXT_KEYMAPS + # <src-var> = extended_keymaps + # <dst-var> = EXT_KEYMAPS + gather_vars() { + local item map value + + # FIXME: double check + # shellcheck disable=SC2068 + for item in "$@"; do + read -r -a item <<< "${item/:/ }" + for map in ${item[1]//,/ }; do + read -r -a map <<< "${map//-/ }" + if [[ -f "$dracutsysrootdir${item[0]}" ]]; then + value=$(grep "^${map[0]}=" "$dracutsysrootdir${item[0]}") + value=${value#*=} + echo "${map[1]:-${map[0]}}=${value}" + fi + unset map + done + done + } + + install_base() { + inst_multiple setfont loadkeys kbd_mode stty + + if ! dracut_module_included "systemd"; then + inst "${moddir}"/console_init.sh /lib/udev/console_init + inst_rules "${moddir}"/10-console.rules + inst_hook cmdline 20 "${moddir}/parse-i18n.sh" + fi + + if [[ ${kbddir} != "/usr/share" ]]; then + inst_dir /usr/share + for _src in "${KBDSUBDIRS[@]}"; do + [ ! -e "${initdir}/usr/share/${_src}" ] && ln -s "${kbddir}/${_src}" "${initdir}/usr/share/${_src}" + done + fi + } + + install_all_kbd() { + local _src _line + + for _src in "${KBDSUBDIRS[@]}"; do + inst_dir "${kbddir}/$_src" + $DRACUT_CP -L -t "${initdir}/${kbddir}/$_src" "${dracutsysrootdir}${kbddir}/$_src"/* + done + + # remove unnecessary files + rm -f -- "${initdir}${kbddir}/consoletrans/utflist" + find "${initdir}${kbddir}/" -name README\* -delete + find "${initdir}${kbddir}/" -name '*.gz' -print -quit \ + | while read -r _line || [ -n "$_line" ]; do + inst_multiple gzip + done + + find "${initdir}${kbddir}/" -name '*.bz2' -print -quit \ + | while read -r _line || [ -n "$_line" ]; do + inst_multiple bzip2 + done + } + + install_local_i18n() { + local map + + # shellcheck disable=SC2086 + eval "$(gather_vars ${i18n_vars})" + # shellcheck disable=SC1090 + [ -f "$dracutsysrootdir"$I18N_CONF ] && . "$dracutsysrootdir"$I18N_CONF + # shellcheck disable=SC1090 + [ -f "$dracutsysrootdir"$VCONFIG_CONF ] && . "$dracutsysrootdir"$VCONFIG_CONF + + shopt -q -s nocasematch + if [[ ${UNICODE} ]]; then + if [[ ${UNICODE} == YES || ${UNICODE} == 1 ]]; then + UNICODE=1 + elif [[ ${UNICODE} == NO || ${UNICODE} == 0 ]]; then + UNICODE=0 + else + UNICODE='' + fi + fi + if [[ ! ${UNICODE} && ${LANG} =~ .*\.UTF-?8 ]]; then + UNICODE=1 + fi + shopt -q -u nocasematch + + # Gentoo user may have KEYMAP set to something like "-u pl2", + KEYMAP=${KEYMAP#-* } + + # openSUSE user may have KEYMAP set to something like ".gz" + KEYMAP=${KEYMAP/.gz/} + + # KEYTABLE is a bit special - it defines base keymap name and UNICODE + # determines whether non-UNICODE or UNICODE version is used + + if [[ ${KEYTABLE} ]]; then + if [[ ${UNICODE} == 1 ]]; then + [[ ${KEYTABLE} =~ .*\.uni.* ]] || KEYTABLE=${KEYTABLE%.map*}.uni + fi + KEYMAP=${KEYTABLE} + fi + + # I'm not sure of the purpose of UNIKEYMAP and GRP_TOGGLE. They were in + # original redhat-i18n module. Anyway it won't hurt. + EXT_KEYMAPS+=\ ${UNIKEYMAP}\ ${GRP_TOGGLE} + + [[ ${KEYMAP} ]] || { + dinfo 'No KEYMAP configured.' + return 1 + } + + findkeymap "${KEYMAP}" + + for map in ${EXT_KEYMAPS}; do + ddebug "Adding extra map: ${map}" + findkeymap "${map}" + done + + for keymap in "${!KEYMAPS[@]}"; do + inst_opt_decompress "${keymap}" + done + + inst_opt_decompress "${kbddir}"/consolefonts/"${DEFAULT_FONT}".* + + if [[ ${FONT} ]] && [[ ${FONT} != "${DEFAULT_FONT}" ]]; then + if [[ -f "${kbddir}"/consolefonts/"${FONT}" ]]; then + inst_opt_decompress "${kbddir}"/consolefonts/"${FONT}" + else + FONT=${FONT%.psf*} + inst_opt_decompress "${kbddir}"/consolefonts/"${FONT}".* + fi + fi + + if [[ ${FONT_MAP} ]]; then + FONT_MAP=${FONT_MAP%.trans} + # There are three different formats that setfont supports + inst_simple "${kbddir}"/consoletrans/"${FONT_MAP}" \ + || inst_simple "${kbddir}"/consoletrans/"${FONT_MAP}".trans \ + || inst_simple "${kbddir}"/consoletrans/"${FONT_MAP}"_to_uni.trans \ + || dwarn "Could not find FONT_MAP ${FONT_MAP}!" + fi + + if [[ ${FONT_UNIMAP} ]]; then + FONT_UNIMAP=${FONT_UNIMAP%.uni} + inst_simple "${kbddir}"/unimaps/"${FONT_UNIMAP}".uni + fi + + if dracut_module_included "systemd" && [[ -f $dracutsysrootdir${I18N_CONF} ]]; then + inst_simple ${I18N_CONF} + else + mksubdirs "${initdir}"${I18N_CONF} + print_vars LC_ALL LANG >> "${initdir}"${I18N_CONF} + fi + + if ! dracut_module_included "systemd"; then + mksubdirs "${initdir}"${VCONFIG_CONF} + print_vars KEYMAP EXT_KEYMAPS UNICODE FONT FONT_MAP FONT_UNIMAP >> "${initdir}"${VCONFIG_CONF} + fi + + return 0 + } + + checks() { + for kbddir in ${kbddir} /usr/lib/kbd /lib/kbd /usr/share /usr/share/kbd; do + if [[ -d "$dracutsysrootdir${kbddir}" ]]; then + for dir in "${KBDSUBDIRS[@]}"; do + [[ -d "$dracutsysrootdir${kbddir}/${dir}" ]] && continue + false + done && break + fi + kbddir='' + done + + [[ "$kbddir" ]] || return 1 + + [[ -f $dracutsysrootdir$I18N_CONF && -f $dracutsysrootdir$VCONFIG_CONF ]] \ + || [[ ! ${hostonly} || ${i18n_vars} ]] || { + derror 'i18n_vars not set! Please set up i18n_vars in ' \ + 'configuration file.' + } + return 0 + } + + if checks; then + install_base + + # https://github.com/dracutdevs/dracut/issues/796 + if dracut_module_included "systemd" && [[ -f $dracutsysrootdir${VCONFIG_CONF} ]]; then + inst_simple ${VCONFIG_CONF} + fi + + if [[ ${hostonly} ]] && ! [[ ${i18n_install_all} == "yes" ]]; then + install_local_i18n || install_all_kbd + else + install_all_kbd + fi + fi +} diff --git a/modules.d/10i18n/parse-i18n.sh b/modules.d/10i18n/parse-i18n.sh new file mode 100755 index 0000000..2deb2c4 --- /dev/null +++ b/modules.d/10i18n/parse-i18n.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +inst_key_val() { + local _value + local _file + local _default + _file="$1" + shift + _key="$1" + shift + _default="$1" + shift + _value="$(getarg "$@")" + [ -z "${_value}" ] && _value=$_default + if [ -n "${_value}" ]; then + printf -- '%s="%s"\n' "${_key}" "${_value}" >> "$_file" + fi + unset _file + unset _value +} + +inst_key_val /etc/vconsole.conf KEYMAP '' rd.vconsole.keymap KEYMAP -d KEYTABLE +inst_key_val /etc/vconsole.conf FONT '' rd.vconsole.font FONT -d SYSFONT +inst_key_val /etc/vconsole.conf FONT_MAP '' rd.vconsole.font.map FONT_MAP -d CONTRANS +inst_key_val /etc/vconsole.conf FONT_UNIMAP '' rd.vconsole.font.unimap FONT_UNIMAP -d UNIMAP +inst_key_val /etc/vconsole.conf UNICODE 1 rd.vconsole.font.unicode UNICODE vconsole.unicode +inst_key_val /etc/vconsole.conf EXT_KEYMAP '' rd.vconsole.keymap.ext EXT_KEYMAP + +inst_key_val /etc/locale.conf LANG '' rd.locale.LANG LANG +inst_key_val /etc/locale.conf LC_ALL '' rd.locale.LC_ALL LC_ALL + +if [ -f /etc/locale.conf ]; then + . /etc/locale.conf + export LANG + export LC_ALL +fi diff --git a/modules.d/30convertfs/convertfs.sh b/modules.d/30convertfs/convertfs.sh new file mode 100755 index 0000000..58fa56d --- /dev/null +++ b/modules.d/30convertfs/convertfs.sh @@ -0,0 +1,193 @@ +#!/bin/bash + +type ismounted > /dev/null 2>&1 || . /lib/dracut-lib.sh + +ROOT="$1" + +if [[ ! -d $ROOT ]]; then + echo "Usage: $0 <rootdir>" + exit 1 +fi + +if [[ $ROOT -ef / ]]; then + echo "Can't convert the running system." + echo "Please boot with 'rd.convertfs' on the kernel command line," + echo "to update with the help of the initramfs," + echo "or run this script from a rescue system." + exit 1 +fi + +while [[ $ROOT != "${ROOT%/}" ]]; do + ROOT=${ROOT%/} +done + +if [ ! -L "$ROOT"/var/run -a -e "$ROOT"/var/run ]; then + echo "Converting /var/run to symlink" + mv -f "$ROOT"/var/run "$ROOT"/var/run.runmove~ + ln -sfn ../run "$ROOT"/var/run +fi + +if [ ! -L "$ROOT"/var/lock -a -e "$ROOT"/var/lock ]; then + echo "Converting /var/lock to symlink" + mv -f "$ROOT"/var/lock "$ROOT"/var/lock.lockmove~ + ln -sfn ../run/lock "$ROOT"/var/lock +fi + +needconvert() { + for dir in "$ROOT/bin" "$ROOT/sbin" "$ROOT/lib" "$ROOT/lib64"; do + if [[ -e $dir ]]; then + [[ -L $dir ]] || return 0 + fi + done + return 1 +} + +if ! [ -e "$ROOT/usr/bin" ]; then + echo "$ROOT/usr/bin does not exist!" + echo "Make sure, the kernel command line has enough information" + echo "to mount /usr (man dracut.cmdline)" + exit 1 +fi + +if ! needconvert; then + echo "Your system is already converted." + exit 0 +fi + +testfile="$ROOT/.usrmovecheck$$" +rm -f -- "$testfile" +: > "$testfile" +if [[ ! -e $testfile ]]; then + echo "Cannot write to $ROOT/" + exit 1 +fi +rm -f -- "$testfile" + +testfile="$ROOT/usr/.usrmovecheck$$" +rm -f -- "$testfile" +: > "$testfile" +if [[ ! -e $testfile ]]; then + echo "Cannot write to $ROOT/usr/" + exit 1 +fi +rm -f -- "$testfile" + +find_mount() { + local dev wanted_dev + wanted_dev="$(readlink -e -q "$1")" + while read -r dev _ || [ -n "$dev" ]; do + [ "$dev" = "$wanted_dev" ] && echo "$dev" && return 0 + done < /proc/mounts + return 1 +} + +# clean up after ourselves no matter how we die. +cleanup() { + echo "Something failed. Move back to the original state" + for dir in "$ROOT/bin" "$ROOT/sbin" "$ROOT/lib" "$ROOT/lib64" \ + "$ROOT/usr/bin" "$ROOT/usr/sbin" "$ROOT/usr/lib" \ + "$ROOT/usr/lib64"; do + [[ -d "${dir}.usrmove-new" ]] && rm -fr -- "${dir}.usrmove-new" + if [[ -d "${dir}.usrmove-old" ]]; then + mv "$dir" "${dir}.del~" + mv "${dir}.usrmove-old" "$dir" + rm -fr -- "${dir}.del~" + fi + done +} + +trap 'ret=$?; [[ $ret -ne 0 ]] && cleanup;exit $ret;' EXIT +trap 'exit 1;' SIGINT + +ismounted "$ROOT/usr" || CP_HARDLINK="-l" + +set -e + +# merge / and /usr in new dir in /usr +for dir in bin sbin lib lib64; do + rm -rf -- "$ROOT/usr/${dir}.usrmove-new" + [[ -L "$ROOT/$dir" ]] && continue + [[ -d "$ROOT/$dir" ]] || continue + echo "Make a copy of \`$ROOT/usr/$dir'." + [[ -d "$ROOT/usr/$dir" ]] \ + && cp -ax -l "$ROOT/usr/$dir" "$ROOT/usr/${dir}.usrmove-new" + echo "Merge the copy with \`$ROOT/$dir'." + [[ -d "$ROOT/usr/${dir}.usrmove-new" ]] \ + || mkdir -p "$ROOT/usr/${dir}.usrmove-new" + cp -axT $CP_HARDLINK --backup --suffix=.usrmove~ "$ROOT/$dir" "$ROOT/usr/${dir}.usrmove-new" + echo "Clean up duplicates in \`$ROOT/usr/$dir'." + # delete all symlinks that have been backed up + find "$ROOT/usr/${dir}.usrmove-new" -type l -name '*.usrmove~' -delete || : + # replace symlink with backed up binary + # shellcheck disable=SC2156 + find "$ROOT/usr/${dir}.usrmove-new" \ + -name '*.usrmove~' \ + -type f \ + -exec bash -c 'p="{}";o=${p%%%%.usrmove~}; + [[ -L "$o" ]] && mv -f "$p" "$o"' ';' || : +done +# switch over merged dirs in /usr +for dir in bin sbin lib lib64; do + [[ -d "$ROOT/usr/${dir}.usrmove-new" ]] || continue + echo "Switch to new \`$ROOT/usr/$dir'." + rm -fr -- "$ROOT/usr/${dir}.usrmove-old" + mv "$ROOT/usr/$dir" "$ROOT/usr/${dir}.usrmove-old" + mv "$ROOT/usr/${dir}.usrmove-new" "$ROOT/usr/$dir" +done + +# replace dirs in / with links to /usr +for dir in bin sbin lib lib64; do + [[ -L "$ROOT/$dir" ]] && continue + [[ -d "$ROOT/$dir" ]] || continue + echo "Create \`$ROOT/$dir' symlink." + rm -fr -- "$ROOT/${dir}.usrmove-old" || : + mv "$ROOT/$dir" "$ROOT/${dir}.usrmove-old" + ln -sfn usr/$dir "$ROOT/$dir" +done + +echo "Clean up backup files." +# everything seems to work; cleanup +for dir in bin sbin lib lib64; do + # if we get killed in the middle of "rm -rf", ensure not to leave + # an incomplete directory, which is moved back by cleanup() + [[ -d "$ROOT/usr/${dir}.usrmove-old" ]] \ + && mv "$ROOT/usr/${dir}.usrmove-old" "$ROOT/usr/${dir}.usrmove-old~" + [[ -d "$ROOT/${dir}.usrmove-old" ]] \ + && mv "$ROOT/${dir}.usrmove-old" "$ROOT/${dir}.usrmove-old~" +done + +for dir in bin sbin lib lib64; do + if [[ -d "$ROOT/usr/${dir}.usrmove-old~" ]]; then + rm -rf -- "$ROOT/usr/${dir}.usrmove-old~" + fi + + if [[ -d "$ROOT/${dir}.usrmove-old~" ]]; then + rm -rf -- "$ROOT/${dir}.usrmove-old~" + fi +done + +for dir in lib lib64; do + [[ -d "$ROOT/$dir" ]] || continue + for lib in "$ROOT"/usr/"${dir}"/lib*.so*.usrmove~; do + [[ -f $lib ]] || continue + mv "$lib" "${lib/.so/_so}" + done +done + +set +e + +echo "Run ldconfig." +ldconfig -r "$ROOT" + +if [[ -f "$ROOT"/etc/selinux/config ]]; then + # shellcheck disable=SC1090 + . "$ROOT"/etc/selinux/config +fi + +if [ -n "$(command -v setfiles)" ] && [ "$SELINUX" != "disabled" ] && [ -f /etc/selinux/"${SELINUXTYPE}"/contexts/files/file_contexts ]; then + echo "Fixing SELinux labels" + setfiles -r "$ROOT" -p /etc/selinux/"${SELINUXTYPE}"/contexts/files/file_contexts "$ROOT"/sbin "$ROOT"/bin "$ROOT"/lib "$ROOT"/lib64 "$ROOT"/usr/lib "$ROOT"/usr/lib64 "$ROOT"/etc/ld.so.cache "$ROOT"/var/cache/ldconfig || : +fi + +echo "Done." +exit 0 diff --git a/modules.d/30convertfs/do-convertfs.sh b/modules.d/30convertfs/do-convertfs.sh new file mode 100755 index 0000000..6ce31cb --- /dev/null +++ b/modules.d/30convertfs/do-convertfs.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if getargbool 0 rd.convertfs; then + if getargbool 0 rd.debug; then + bash -x convertfs "$NEWROOT" 2>&1 | vinfo + else + convertfs "$NEWROOT" 2>&1 | vinfo + fi +fi diff --git a/modules.d/30convertfs/module-setup.sh b/modules.d/30convertfs/module-setup.sh new file mode 100755 index 0000000..68fb78f --- /dev/null +++ b/modules.d/30convertfs/module-setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# called by dracut +check() { + [[ $mount_needs ]] && return 1 + return 255 +} + +# called by dracut +depends() { + echo base bash +} + +# called by dracut +install() { + inst_multiple bash find ldconfig mv rm cp ln + inst_hook pre-pivot 99 "$moddir/do-convertfs.sh" + inst_script "$moddir/convertfs.sh" /usr/bin/convertfs +} diff --git a/modules.d/35connman/cm-config.sh b/modules.d/35connman/cm-config.sh new file mode 100755 index 0000000..6ae754a --- /dev/null +++ b/modules.d/35connman/cm-config.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +type cm_generate_connections > /dev/null 2>&1 || . /lib/cm-lib.sh + +if [ -n "$netroot" ] || [ -e /tmp/net.ifaces ]; then + echo rd.neednet >> /etc/cmdline.d/connman.conf +fi + +if getargbool 0 rd.debug -d -y rdinitdebug -d -y rdnetdebug; then + if [ -n "$DRACUT_SYSTEMD" ]; then + # Enable tty output if a usable console is found + # shellcheck disable=SC2217 + if [ -w /dev/console ] && (echo < /dev/console) > /dev/null 2> /dev/null; then + mkdir -p /run/systemd/system/cm-initrd.service.d + cat << EOF > /run/systemd/system/cm-initrd.service.d/tty-output.conf +[Service] +StandardOutput=tty +EOF + systemctl --no-block daemon-reload + fi + fi +fi + +cm_generate_connections diff --git a/modules.d/35connman/cm-initrd.service b/modules.d/35connman/cm-initrd.service new file mode 100644 index 0000000..cecb408 --- /dev/null +++ b/modules.d/35connman/cm-initrd.service @@ -0,0 +1,24 @@ +[Unit] +DefaultDependencies=no +Wants=systemd-udev-trigger.service +After=systemd-udev-trigger.service +After=dracut-cmdline.service +Wants=network.target +Before=network.target +RequiresMountsFor=/var/lib/connman +After=dbus.service +ConditionPathExists=/run/connman/initrd/neednet + +[Service] +Type=dbus +BusName=net.connman +Restart=on-failure +ExecStart=/usr/sbin/connmand -n +StandardOutput=null +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_ADMIN +ProtectHome=true +ProtectSystem=full + +[Install] +WantedBy=initrd.target +Also=cm-wait-online-initrd.service diff --git a/modules.d/35connman/cm-lib.sh b/modules.d/35connman/cm-lib.sh new file mode 100755 index 0000000..69c4fa2 --- /dev/null +++ b/modules.d/35connman/cm-lib.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +type getcmdline > /dev/null 2>&1 || . /lib/dracut-lib.sh + +cm_generate_connections() { + if getargbool 0 rd.neednet; then + mkdir -p "$hookdir"/initqueue/finished + echo '[ -f /tmp/cm.done ]' > "$hookdir"/initqueue/finished/cm.sh + mkdir -p /run/connman/initrd + : > /run/connman/initrd/neednet # activate ConnMan services + fi +} diff --git a/modules.d/35connman/cm-run.sh b/modules.d/35connman/cm-run.sh new file mode 100755 index 0000000..a9dcf05 --- /dev/null +++ b/modules.d/35connman/cm-run.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +type source_hook > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if [ -e /tmp/cm.done ]; then + return +fi + +while read -r _serv; do + ifname=$(connmanctl services "$_serv" | grep Interface= | sed 's/^.*Interface=\([^,]*\).*$/\1/') + source_hook initqueue/online "$ifname" + /sbin/netroot "$ifname" +done < <(connmanctl services | grep -oE '[^ ]+$') + +: > /tmp/cm.done diff --git a/modules.d/35connman/cm-wait-online-initrd.service b/modules.d/35connman/cm-wait-online-initrd.service new file mode 100644 index 0000000..08e6941 --- /dev/null +++ b/modules.d/35connman/cm-wait-online-initrd.service @@ -0,0 +1,16 @@ +[Unit] +DefaultDependencies=no +Requires=cm-initrd.service +After=cm-initrd.service +Before=network-online.target +Before=dracut-initqueue.service +ConditionPathExists=/run/connman/initrd/neednet + +[Service] +Type=oneshot +ExecStart=/usr/sbin/connmand-wait-online +RemainAfterExit=yes + +[Install] +WantedBy=initrd.target +WantedBy=network-online.target diff --git a/modules.d/35connman/module-setup.sh b/modules.d/35connman/module-setup.sh new file mode 100755 index 0000000..6502d30 --- /dev/null +++ b/modules.d/35connman/module-setup.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# called by dracut +check() { + require_binaries sed grep connmand connmanctl connmand-wait-online || return 1 + + # do not add this module by default + return 255 +} + +# called by dracut +depends() { + echo dbus systemd bash + return 0 +} + +# called by dracut +installkernel() { + return 0 +} + +# called by dracut +install() { + # We don't need `ip` but having it is *really* useful for people debugging + # in an emergency shell. + inst_multiple ip sed grep + + inst_script "$moddir/netroot.sh" "/sbin/netroot" + inst connmand + inst connmanctl + inst connmand-wait-online + inst "$dbussystem"/connman.conf + [[ $hostonly ]] && [[ -f $dracutsysrootdir/etc/connman/main.conf ]] && inst /etc/connman/main.conf + inst_dir /usr/lib/connman/plugins + inst_dir /var/lib/connman + + inst_hook cmdline 99 "$moddir/cm-config.sh" + + inst_simple "$moddir"/cm-initrd.service "$systemdsystemunitdir"/cm-initrd.service + inst_simple "$moddir"/cm-wait-online-initrd.service "$systemdsystemunitdir"/cm-wait-online-initrd.service + + $SYSTEMCTL -q --root "$initdir" enable cm-initrd.service + + inst_hook initqueue/settled 99 "$moddir/cm-run.sh" + + inst_simple "$moddir/cm-lib.sh" "/lib/cm-lib.sh" +} diff --git a/modules.d/35connman/netroot.sh b/modules.d/35connman/netroot.sh new file mode 100755 index 0000000..8f97774 --- /dev/null +++ b/modules.d/35connman/netroot.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin +command -v getarg > /dev/null || . /lib/dracut-lib.sh +command -v setup_net > /dev/null || . /lib/net-lib.sh + +# Huh? Empty $1? +[ -z "$1" ] && exit 1 + +# [ ! -z $2 ] means this is for manually bringing up network +# instead of real netroot; If It's called without $2, then there's +# no sense in doing something if no (net)root info is available +# or root is already there +[ -d "$NEWROOT"/proc ] && exit 0 + +if [ -z "$netroot" ]; then + netroot=$(getarg netroot=) +fi + +[ -z "$netroot" ] && exit 1 + +# Set or override primary interface +netif=$1 +[ -e "/tmp/net.bootdev" ] && read -r netif < /tmp/net.bootdev + +case "$netif" in + ??:??:??:??:??:??) # MAC address + for i in /sys/class/net/*/address; do + read -r mac < "$i" + if [ "$mac" = "$netif" ]; then + i=${i%/address} + netif=${i##*/} + break + fi + done ;; +esac + +# Figure out the handler for root=dhcp by recalling all netroot cmdline +# handlers when this is not called from manually network bringing up. +if [ -z "$2" ]; then + if getarg "root=dhcp" || getarg "netroot=dhcp" || getarg "root=dhcp6" || getarg "netroot=dhcp6"; then + # Load dhcp options + # shellcheck disable=SC1090 + [ -e /tmp/dhclient."$netif".dhcpopts ] && . /tmp/dhclient."$netif".dhcpopts + + # If we have a specific bootdev with no dhcpoptions or empty root-path, + # we die. Otherwise we just warn + if [ -z "$new_root_path" ]; then + [ -n "$BOOTDEV" ] && die "No dhcp root-path received for '$BOOTDEV'" + warn "No dhcp root-path received for '$netif' trying other interfaces if available" + exit 1 + fi + + rm -f -- "$hookdir"/initqueue/finished/dhcp.sh + + # Set netroot to new_root_path, so cmdline parsers don't call + netroot=$new_root_path + + # FIXME! + unset rootok + for f in "$hookdir"/cmdline/90*.sh; do + # shellcheck disable=SC1090 + [ -f "$f" ] && . "$f" + done + else + rootok="1" + fi + + # Check: do we really know how to handle (net)root? + if [ -z "$root" ]; then + root=$(getarg root=) + fi + [ -z "$root" ] && die "No or empty root= argument" + [ -z "$rootok" ] && die "Don't know how to handle 'root=$root'" + + handler=${netroot%%:*} + handler=${handler%%4} + handler=$(command -v "${handler}"root) + if [ -z "$netroot" ] || [ ! -e "$handler" ]; then + die "No handler for netroot type '$netroot'" + fi +fi + +# Source netroot hooks before we start the handler +source_hook netroot "$netif" + +# Run the handler; don't store the root, it may change from device to device +# XXX other variables to export? +[ -n "$handler" ] && "$handler" "$netif" "$netroot" "$NEWROOT" +save_netinfo "$netif" + +exit 0 diff --git a/modules.d/35network-legacy/dhclient-script.sh b/modules.d/35network-legacy/dhclient-script.sh new file mode 100755 index 0000000..b3e5e75 --- /dev/null +++ b/modules.d/35network-legacy/dhclient-script.sh @@ -0,0 +1,275 @@ +#!/bin/sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +type ip_to_var > /dev/null 2>&1 || . /lib/net-lib.sh + +# We already need a set netif here +netif=$interface + +setup_interface() { + ip=$new_ip_address + mtu=$new_interface_mtu + mask=$new_subnet_mask + bcast=$new_broadcast_address + gw=${new_routers%%,*} + domain=$new_domain_name + # get rid of control chars + search=$(printf -- "%s" "$new_domain_search" | tr -d '[:cntrl:]') + namesrv=$new_domain_name_servers + hostname=$new_host_name + [ -n "$new_dhcp_lease_time" ] && lease_time=$new_dhcp_lease_time + [ -n "$new_max_life" ] && lease_time=$new_max_life + preferred_lft=$lease_time + [ -n "$new_preferred_life" ] && preferred_lft=$new_preferred_life + + # shellcheck disable=SC1090 + [ -f /tmp/net."$netif".override ] && . /tmp/net."$netif".override + + # Taken from debian dhclient-script: + # The 576 MTU is only used for X.25 and dialup connections + # where the admin wants low latency. Such a low MTU can cause + # problems with UDP traffic, among other things. As such, + # disallow MTUs from 576 and below by default, so that broken + # MTUs are ignored, but higher stuff is allowed (1492, 1500, etc). + if [ -n "$mtu" ] && [ "$mtu" -gt 576 ]; then + if ! ip link set "$netif" mtu "$mtu"; then + ip link set "$netif" down + ip link set "$netif" mtu "$mtu" + linkup "$netif" + fi + fi + + ip addr add "$ip"${mask:+/$mask} ${bcast:+broadcast $bcast} dev "$netif" \ + ${lease_time:+valid_lft $lease_time} \ + ${preferred_lft:+preferred_lft ${preferred_lft}} + + if [ -n "$gw" ]; then + if [ "$mask" = "255.255.255.255" ]; then + # point-to-point connection => set explicit route to gateway + echo ip route add "$gw" dev "$netif" > /tmp/net."$netif".gw + fi + + echo "$gw" | { + IFS=' ' read -r main_gw other_gw + echo ip route replace default via "$main_gw" dev "$netif" >> /tmp/net."$netif".gw + if [ -n "$other_gw" ]; then + for g in $other_gw; do + echo ip route add default via "$g" dev "$netif" >> /tmp/net."$netif".gw + done + fi + } + fi + + if getargbool 1 rd.peerdns; then + [ -n "${search}${domain}" ] && echo "search $search $domain" > /tmp/net."$netif".resolv.conf + if [ -n "$namesrv" ]; then + for s in $namesrv; do + echo nameserver "$s" + done + fi >> /tmp/net."$netif".resolv.conf + fi + # Note: hostname can be fqdn OR short hostname, so chop off any + # trailing domain name and explicitly add any domain if set. + [ -n "$hostname" ] && echo "echo ${hostname%."$domain"}${domain:+.$domain} > /proc/sys/kernel/hostname" > /tmp/net."$netif".hostname +} + +setup_interface6() { + domain=$new_domain_name + # get rid of control chars + search=$(printf -- "%s" "$new_dhcp6_domain_search" | tr -d '[:cntrl:]') + namesrv=$new_dhcp6_name_servers + hostname=$new_host_name + [ -n "$new_dhcp_lease_time" ] && lease_time=$new_dhcp_lease_time + [ -n "$new_max_life" ] && lease_time=$new_max_life + preferred_lft=$lease_time + [ -n "$new_preferred_life" ] && preferred_lft=$new_preferred_life + + # shellcheck disable=SC1090 + [ -f /tmp/net."$netif".override ] && . /tmp/net."$netif".override + + ip -6 addr add "${new_ip6_address}"/"${new_ip6_prefixlen}" \ + dev "${netif}" scope global \ + ${lease_time:+valid_lft $lease_time} \ + ${preferred_lft:+preferred_lft ${preferred_lft}} + + if getargbool 1 rd.peerdns; then + [ -n "${search}${domain}" ] && echo "search $search $domain" > /tmp/net."$netif".resolv.conf + if [ -n "$namesrv" ]; then + for s in $namesrv; do + echo nameserver "$s" + done + fi >> /tmp/net."$netif".resolv.conf + fi + + # Note: hostname can be fqdn OR short hostname, so chop off any + # trailing domain name and explicitly add any domain if set. + [ -n "$hostname" ] && echo "echo ${hostname%."$domain"}${domain:+.$domain} > /proc/sys/kernel/hostname" > /tmp/net."$netif".hostname +} + +parse_option_121() { + while [ $# -ne 0 ]; do + mask="$1" + shift + + # Is the destination a multicast group? + if [ "$1" -ge 224 -a "$1" -lt 240 ]; then + multicast=1 + else + multicast=0 + fi + + # Parse the arguments into a CIDR net/mask string + if [ "$mask" -gt 24 ]; then + destination="$1.$2.$3.$4/$mask" + shift + shift + shift + shift + elif [ "$mask" -gt 16 ]; then + destination="$1.$2.$3.0/$mask" + shift + shift + shift + elif [ "$mask" -gt 8 ]; then + destination="$1.$2.0.0/$mask" + shift + shift + elif [ "$mask" -gt 0 ]; then + destination="$1.0.0.0/$mask" + shift + else + destination="0.0.0.0/$mask" + fi + + # Read the gateway + gateway="$1.$2.$3.$4" + shift + shift + shift + shift + + # Multicast routing on Linux + # - If you set a next-hop address for a multicast group, this breaks with Cisco switches + # - If you simply leave it link-local and attach it to an interface, it works fine. + if [ $multicast -eq 1 -o "$gateway" = "0.0.0.0" ]; then + temp_result="$destination dev $interface" + else + temp_result="$destination via $gateway dev $interface" + fi + + echo "/sbin/ip route replace $temp_result" + done +} + +case $reason in + PREINIT) + echo "dhcp: PREINIT $netif up" + linkup "$netif" + ;; + + PREINIT6) + echo "dhcp: PREINIT6 $netif up" + linkup "$netif" + wait_for_ipv6_dad_link "$netif" + ;; + + BOUND) + echo "dhcp: BOUND setting up $netif" + unset layer2 + if [ -f /sys/class/net/"$netif"/device/layer2 ]; then + read -r layer2 < /sys/class/net/"$netif"/device/layer2 + fi + if [ "$layer2" != "0" ]; then + if command -v arping2 > /dev/null; then + if arping2 -q -C 1 -c 2 -I "$netif" -0 "$new_ip_address"; then + warn "Duplicate address detected for $new_ip_address while doing dhcp. retrying" + exit 1 + fi + else + if ! arping -f -q -D -c 2 -I "$netif" "$new_ip_address"; then + warn "Duplicate address detected for $new_ip_address while doing dhcp. retrying" + exit 1 + fi + fi + fi + unset layer2 + setup_interface + set | while read -r line || [ -n "$line" ]; do + [ "${line#new_}" = "$line" ] && continue + echo "$line" + done > /tmp/dhclient."$netif".dhcpopts + + { + echo '. /lib/net-lib.sh' + echo "setup_net $netif" + if [ -n "$new_classless_static_routes" ]; then + OLDIFS="$IFS" + IFS=".$IFS" + parse_option_121 "$new_classless_static_routes" + IFS="$OLDIFS" + fi + echo "source_hook initqueue/online $netif" + [ -e /tmp/net."$netif".manualup ] || echo "/sbin/netroot $netif" + echo "rm -f -- $hookdir/initqueue/setup_net_$netif.sh" + } > "$hookdir"/initqueue/setup_net_"$netif".sh + + echo "[ -f /tmp/net.$netif.did-setup ]" > "$hookdir"/initqueue/finished/dhclient-"$netif".sh + : > /tmp/net."$netif".up + if [ -e /sys/class/net/"${netif}"/address ]; then + : > "/tmp/net.$(cat /sys/class/net/"${netif}"/address).up" + fi + + ;; + + RENEW | REBIND) + unset lease_time + [ -n "$new_dhcp_lease_time" ] && lease_time=$new_dhcp_lease_time + [ -n "$new_max_life" ] && lease_time=$new_max_life + preferred_lft=$lease_time + [ -n "$new_preferred_life" ] && preferred_lft=$new_preferred_life + ip -4 addr change "${new_ip_address}"/"${new_subnet_mask}" broadcast "${new_broadcast_address}" dev "${interface}" \ + ${lease_time:+valid_lft $lease_time} ${preferred_lft:+preferred_lft ${preferred_lft}} \ + > /dev/null 2>&1 + ;; + + BOUND6) + echo "dhcp: BOUND6 setting up $netif" + setup_interface6 + + set | while read -r line || [ -n "$line" ]; do + [ "${line#new_}" = "$line" ] && continue + echo "$line" + done > /tmp/dhclient."$netif".dhcpopts + + { + echo '. /lib/net-lib.sh' + echo "setup_net $netif" + echo "source_hook initqueue/online $netif" + [ -e /tmp/net."$netif".manualup ] || echo "/sbin/netroot $netif" + echo "rm -f -- $hookdir/initqueue/setup_net_$netif.sh" + } > "$hookdir"/initqueue/setup_net_"$netif".sh + + echo "[ -f /tmp/net.$netif.did-setup ]" > "$hookdir"/initqueue/finished/dhclient-"$netif".sh + : > /tmp/net."$netif".up + if [ -e /sys/class/net/"${netif}"/address ]; then + : > "/tmp/net.$(cat /sys/class/net/"${netif}"/address).up" + fi + ;; + + RENEW6 | REBIND6) + unset lease_time + [ -n "$new_dhcp_lease_time" ] && lease_time=$new_dhcp_lease_time + [ -n "$new_max_life" ] && lease_time=$new_max_life + preferred_lft=$lease_time + [ -n "$new_preferred_life" ] && preferred_lft=$new_preferred_life + ip -6 addr change "${new_ip6_address}"/"${new_ip6_prefixlen}" dev "${interface}" scope global \ + ${lease_time:+valid_lft $lease_time} ${preferred_lft:+preferred_lft ${preferred_lft}} \ + > /dev/null 2>&1 + ;; + + *) echo "dhcp: $reason" ;; +esac + +exit 0 diff --git a/modules.d/35network-legacy/dhclient.conf b/modules.d/35network-legacy/dhclient.conf new file mode 100644 index 0000000..ffd24ef --- /dev/null +++ b/modules.d/35network-legacy/dhclient.conf @@ -0,0 +1,11 @@ + +option classless-static-routes code 121 = array of unsigned integer 8; + +send dhcp-client-identifier = hardware; + +request subnet-mask, broadcast-address, time-offset, routers, + domain-name, domain-name-servers, domain-search, host-name, + root-path, interface-mtu, classless-static-routes, + netbios-name-servers, netbios-scope, ntp-servers, + dhcp6.domain-search, dhcp6.fqdn, + dhcp6.name-servers, dhcp6.sntp-servers; diff --git a/modules.d/35network-legacy/dhcp-multi.sh b/modules.d/35network-legacy/dhcp-multi.sh new file mode 100755 index 0000000..60e0374 --- /dev/null +++ b/modules.d/35network-legacy/dhcp-multi.sh @@ -0,0 +1,133 @@ +#!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +# +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +# File to start dhclient requests on different interfaces in parallel + +. /lib/dracut-lib.sh +. /lib/net-lib.sh + +netif=$1 +do_vlan=$2 +arg=$3 + +# Run dhclient in parallel +do_dhclient() { + local _COUNT=0 + local _timeout + local _DHCPRETRY + _timeout=$(getarg rd.net.timeout.dhcp=) + _DHCPRETRY=$(getargnum 1 1 1000000000 rd.net.dhcp.retry=) + + if [ -n "$_timeout" ]; then + if ! (dhclient --help 2>&1 | grep -q -F -- '--timeout' 2> /dev/null); then + warn "rd.net.timeout.dhcp has no effect because dhclient does not implement the --timeout option" + unset _timeout + fi + fi + + while [ $_COUNT -lt "$_DHCPRETRY" ]; do + info "Starting dhcp for interface $netif" + dhclient "$arg" \ + ${_timeout:+--timeout "$_timeout"} \ + -q \ + -1 \ + -cf /etc/dhclient.conf \ + -pf /tmp/dhclient."$netif".pid \ + -lf /tmp/dhclient."$netif".lease \ + "$netif" & + wait $! 2> /dev/null + + # wait will return the return value of dhclient + retv=$? + + # dhclient and hence wait returned success, 0. + if [ $retv -eq 0 ]; then + return 0 + fi + + # If dhclient exited before wait was called, or it was killed by + # another thread for interface whose DHCP succeeded, then it will not + # find the process with that pid and return error code 127. In that + # case we need to check if /tmp/dhclient.$netif.lease exists. If it + # does, it means dhclient finished executing before wait was called, + # and it was successful (return 0). If /tmp/dhclient.$netif.lease + # does not exist, then it means dhclient was killed by another thread + # or it finished execution but failed dhcp on that interface. + + if [ $retv -eq 127 ]; then + read -r pid < /tmp/dhclient."$netif".pid + info "PID $pid was not found by wait for $netif" + if [ -e /tmp/dhclient."$netif".lease ]; then + info "PID $pid not found but DHCP successful on $netif" + return 0 + fi + fi + + _COUNT=$((_COUNT + 1)) + [ $_COUNT -lt "$_DHCPRETRY" ] && sleep 1 + done + warn "dhcp for interface $netif failed" + # nuke those files since we failed; we might retry dhcp again if it's e.g. + # `ip=dhcp,dhcp6` and we check for the PID file earlier + rm -f /tmp/dhclient."$netif".pid /tmp/dhclient."$netif".lease + return 1 +} + +do_dhclient +ret=$? + +# setup nameserver +for s in "$dns1" "$dns2" $(getargs nameserver); do + [ -n "$s" ] || continue + echo nameserver "$s" >> /tmp/net."$netif".resolv.conf +done + +if [ $ret -eq 0 ]; then + : > /tmp/net."${netif}".up + + if [ -z "$do_vlan" ] && [ -e /sys/class/net/"${netif}"/address ]; then + : > "/tmp/net.$(cat /sys/class/net/"${netif}"/address).up" + fi + + # Check if DHCP also succeeded on another interface before this one. + # We will always use the first one on which DHCP succeeded, by using + # a common file $IFNETFILE, to synchronize between threads. + # Consider the race condition in which multiple threads + # corresponding to different interfaces may try to read $IFNETFILE + # and find it does not exist; they may all end up thinking they are the + # first to succeed (hence more than one thread may end up writing to + # $IFNETFILE). To take care of this, instead of checking if $IFNETFILE + # exists to determine if we are the first, we create a symbolic link + # in $IFNETFILE, pointing to the interface name ($netif), thus storing + # the interface name in the link pointer. + # Creating a link will fail, if the link already exists, hence kernel + # will take care of allowing only first thread to create link, which + # takes care of the race condition for us. Subsequent threads will fail. + # Also, the link points to the interface name, which will tell us which + # interface succeeded. + + if ln -s "$netif" "$IFNETFILE" 2> /dev/null; then + intf=$(readlink "$IFNETFILE") + if [ -e /tmp/dhclient."$intf".lease ]; then + info "DHCP successful on interface $intf" + # Kill all existing dhclient calls for other interfaces, since we + # already got one successful interface + + read -r npid < /tmp/dhclient."$netif".pid + pidlist=$(pgrep dhclient) + for pid in $pidlist; do + [ "$pid" -eq "$npid" ] && continue + kill -9 "$pid" > /dev/null 2>&1 + done + else + echo "ERROR! $IFNETFILE exists but /tmp/dhclient.$intf.lease does not exist!!!" + fi + else + info "DHCP success on $netif, and also on $intf" + exit 0 + fi + exit $ret +fi diff --git a/modules.d/35network-legacy/ifup.sh b/modules.d/35network-legacy/ifup.sh new file mode 100755 index 0000000..3b54b6c --- /dev/null +++ b/modules.d/35network-legacy/ifup.sh @@ -0,0 +1,563 @@ +#!/bin/sh +# +# We don't need to check for ip= errors here, that is handled by the +# cmdline parser script +# +# without $2 means this is for real netroot case +# or it is for manually bring up network ie. for kdump scp vmcore +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +type ip_to_var > /dev/null 2>&1 || . /lib/net-lib.sh + +# Huh? No $1? +[ -z "$1" ] && exit 1 + +# $netif reads easier than $1 +netif=$1 + +# loopback is always handled the same way +if [ "$netif" = "lo" ]; then + ip link set lo up + ip addr add 127.0.0.1/8 dev lo + exit 0 +fi + +do_dhcp_parallel() { + # dhclient-script will mark the netif up and generate the online + # event for nfsroot + # XXX add -V vendor class and option parsing per kernel + + [ -e "/tmp/dhclient.$netif.pid" ] && return 0 + + if ! iface_has_carrier "$netif"; then + warn "No carrier detected on interface $netif" + return 1 + fi + + bootintf=$(readlink "$IFNETFILE") + if [ -n "$bootintf" ] && [ -e "/tmp/dhclient.${bootintf}.lease" ]; then + info "DHCP already succeeded for $bootintf, exiting for $netif" + return 1 + fi + + if [ ! -e /run/NetworkManager/conf.d/10-dracut-dhclient.conf ]; then + mkdir -p /run/NetworkManager/conf.d + echo '[main]' > /run/NetworkManager/conf.d/10-dracut-dhclient.conf + echo 'dhcp=dhclient' >> /run/NetworkManager/conf.d/10-dracut-dhclient.conf + fi + + chmod +x /sbin/dhcp-multi.sh + /sbin/dhcp-multi.sh "$netif" "$DO_VLAN" "$@" & + return 0 +} + +# Run dhclient +do_dhcp() { + # dhclient-script will mark the netif up and generate the online + # event for nfsroot + # XXX add -V vendor class and option parsing per kernel + + local _COUNT + local _timeout + local _DHCPRETRY + + _COUNT=0 + _timeout=$(getarg rd.net.timeout.dhcp=) + _DHCPRETRY=$(getargnum 1 1 1000000000 rd.net.dhcp.retry=) + + [ -e "/tmp/dhclient.${netif}.pid" ] && return 0 + + if ! iface_has_carrier "$netif"; then + warn "No carrier detected on interface $netif" + return 1 + fi + + if [ -n "$_timeout" ]; then + if ! (dhclient --help 2>&1 | grep -q -F -- '--timeout' 2> /dev/null); then + warn "rd.net.timeout.dhcp has no effect because dhclient does not implement the --timeout option" + unset _timeout + fi + fi + + if [ ! -e /run/NetworkManager/conf.d/10-dracut-dhclient.conf ]; then + mkdir -p /run/NetworkManager/conf.d + echo '[main]' > /run/NetworkManager/conf.d/10-dracut-dhclient.conf + echo 'dhcp=dhclient' >> /run/NetworkManager/conf.d/10-dracut-dhclient.conf + fi + + while [ "$_COUNT" -lt "$_DHCPRETRY" ]; do + info "Starting dhcp for interface $netif" + dhclient "$@" \ + ${_timeout:+--timeout "$_timeout"} \ + -q \ + -1 \ + -cf /etc/dhclient.conf \ + -pf "/tmp/dhclient.${netif}.pid" \ + -lf "/tmp/dhclient.${netif}.lease" \ + "$netif" \ + && return 0 + _COUNT=$((_COUNT + 1)) + [ "$_COUNT" -lt "$_DHCPRETRY" ] && sleep 1 + done + warn "dhcp for interface $netif failed" + # nuke those files since we failed; we might retry dhcp again if it's e.g. + # `ip=dhcp,dhcp6` and we check for the PID file at the top + rm -f /tmp/dhclient."$netif".pid /tmp/dhclient."$netif".lease + return 1 +} + +load_ipv6() { + [ -d /proc/sys/net/ipv6 ] && return + modprobe ipv6 + i=0 + while [ ! -d /proc/sys/net/ipv6 ]; do + i=$((i + 1)) + [ $i -gt 10 ] && break + sleep 0.1 + done +} + +do_ipv6auto() { + local ret + load_ipv6 + echo 0 > /proc/sys/net/ipv6/conf/"${netif}"/forwarding + echo 1 > /proc/sys/net/ipv6/conf/"${netif}"/accept_ra + echo 1 > /proc/sys/net/ipv6/conf/"${netif}"/accept_redirects + linkup "$netif" + wait_for_ipv6_auto "$netif" + ret=$? + + [ -n "$hostname" ] && echo "echo $hostname > /proc/sys/kernel/hostname" > "/tmp/net.${netif}.hostname" + + return "$ret" +} + +do_ipv6link() { + local ret + load_ipv6 + echo 0 > /proc/sys/net/ipv6/conf/"${netif}"/forwarding + echo 0 > /proc/sys/net/ipv6/conf/"${netif}"/accept_ra + echo 0 > /proc/sys/net/ipv6/conf/"${netif}"/accept_redirects + linkup "$netif" + + [ -n "$hostname" ] && echo "echo $hostname > /proc/sys/kernel/hostname" > "/tmp/net.${netif}.hostname" + + return "$ret" +} + +# Handle static ip configuration +do_static() { + strglobin "$ip" '*:*:*' && load_ipv6 + + if ! iface_has_carrier "$netif"; then + warn "No carrier detected on interface $netif" + return 1 + elif ! linkup "$netif"; then + warn "Could not bring interface $netif up!" + return 1 + fi + + ip route get "$ip" 2> /dev/null | { + read -r a rest + if [ "$a" = "local" ]; then + warn "Not assigning $ip to interface $netif, cause it is already assigned!" + return 1 + fi + return 0 + } || return 1 + + [ -n "$macaddr" ] && ip link set address "$macaddr" dev "$netif" + [ -n "$mtu" ] && ip link set mtu "$mtu" dev "$netif" + if strglobin "$ip" '*:*:*'; then + # note no ip addr flush for ipv6 + ip addr add "$ip/$mask" ${srv:+peer "$srv"} dev "$netif" + echo 0 > /proc/sys/net/ipv6/conf/"${netif}"/forwarding + echo 1 > /proc/sys/net/ipv6/conf/"${netif}"/accept_ra + echo 1 > /proc/sys/net/ipv6/conf/"${netif}"/accept_redirects + wait_for_ipv6_dad "$netif" + else + if [ -z "$srv" ]; then + if command -v arping2 > /dev/null; then + if arping2 -q -C 1 -c 2 -I "$netif" -0 "$ip"; then + warn "Duplicate address detected for $ip for interface $netif." + return 1 + fi + else + if ! arping -f -q -D -c 2 -I "$netif" "$ip"; then + warn "Duplicate address detected for $ip for interface $netif." + return 1 + fi + fi + fi + ip addr flush dev "$netif" + ip addr add "$ip/$mask" ${srv:+peer "$srv"} brd + dev "$netif" + fi + + [ -n "$gw" ] && echo "ip route replace default via '$gw' dev '$netif'" > "/tmp/net.$netif.gw" + [ -n "$hostname" ] && echo "echo '$hostname' > /proc/sys/kernel/hostname" > "/tmp/net.$netif.hostname" + + return 0 +} + +get_vid() { + case "$1" in + vlan*) + echo "${1#vlan}" + ;; + *.*) + echo "${1##*.}" + ;; + esac +} + +# check, if we need VLAN's for this interface +if [ -z "$DO_VLAN_PHY" ] && [ -e "/tmp/vlan.${netif}.phy" ]; then + unset DO_VLAN + NO_AUTO_DHCP=yes DO_VLAN_PHY=yes ifup "$netif" + modprobe -b -q 8021q + + for i in /tmp/vlan.*."${netif}"; do + [ -e "$i" ] || continue + unset vlanname + unset phydevice + # shellcheck disable=SC1090 + . "$i" + if [ -n "$vlanname" ]; then + linkup "$phydevice" + ip link add dev "$vlanname" link "$phydevice" type vlan id "$(get_vid "$vlanname")" + ifup "$vlanname" + fi + done + exit 0 +fi + +# Check, if interface is VLAN interface +if ! [ -e "/tmp/vlan.${netif}.phy" ]; then + for i in "/tmp/vlan.${netif}".*; do + [ -e "$i" ] || continue + export DO_VLAN=yes + break + done +fi + +# bridge this interface? +if [ -z "$NO_BRIDGE_MASTER" ]; then + for i in /tmp/bridge.*.info; do + [ -e "$i" ] || continue + unset bridgeslaves + unset bridgename + # shellcheck disable=SC1090 + . "$i" + for ethname in $bridgeslaves; do + [ "$netif" != "$ethname" ] && continue + + NO_BRIDGE_MASTER=yes NO_AUTO_DHCP=yes ifup "$ethname" + linkup "$ethname" + if [ ! -e "/tmp/bridge.$bridgename.up" ]; then + ip link add name "$bridgename" type bridge + echo 0 > "/sys/devices/virtual/net/$bridgename/bridge/forward_delay" + : > "/tmp/bridge.$bridgename.up" + fi + ip link set dev "$ethname" master "$bridgename" + ifup "$bridgename" + exit 0 + done + done +fi + +# enslave this interface to bond? +if [ -z "$NO_BOND_MASTER" ]; then + for i in /tmp/bond.*.info; do + [ -e "$i" ] || continue + unset bondslaves + unset bondname + # shellcheck disable=SC1090 + . "$i" + for testslave in $bondslaves; do + [ "$netif" != "$testslave" ] && continue + + # already setup + [ -e "/tmp/bond.$bondname.up" ] && exit 0 + + # wait for all slaves to show up + for slave in $bondslaves; do + # try to create the slave (maybe vlan or bridge) + NO_BOND_MASTER=yes NO_AUTO_DHCP=yes ifup "$slave" + + if ! ip link show dev "$slave" > /dev/null 2>&1; then + # wait for the last slave to show up + exit 0 + fi + done + + modprobe -q -b bonding + echo "+$bondname" > /sys/class/net/bonding_masters 2> /dev/null + ip link set "$bondname" down + + # Stolen from ifup-eth + # add the bits to setup driver parameters here + for arg in $bondoptions; do + key=${arg%%=*} + value=${arg##*=} + # %{value:0:1} is replaced with non-bash specific construct + if [ "${key}" = "arp_ip_target" -a "${#value}" != "0" -a "+${value%%+*}" != "+" ]; then + OLDIFS=$IFS + IFS=',' + for arp_ip in $value; do + echo "+$arp_ip" > "/sys/class/net/${bondname}/bonding/$key" + done + IFS=$OLDIFS + else + echo "$value" > "/sys/class/net/${bondname}/bonding/$key" + fi + done + + linkup "$bondname" + + for slave in $bondslaves; do + cat "/sys/class/net/$slave/address" > "/tmp/net.${bondname}.${slave}.hwaddr" + ip link set "$slave" down + echo "+$slave" > "/sys/class/net/$bondname/bonding/slaves" + linkup "$slave" + done + + # Set mtu on bond master + [ -n "$bondmtu" ] && ip link set mtu "$bondmtu" dev "$bondname" + + # add the bits to setup the needed post enslavement parameters + for arg in $bondoptions; do + key=${arg%%=*} + value=${arg##*=} + if [ "${key}" = "primary" ]; then + echo "$value" > "/sys/class/net/${bondname}/bonding/$key" + fi + done + + : > "/tmp/bond.$bondname.up" + + NO_BOND_MASTER=yes ifup "$bondname" + exit $? + done + done +fi + +if [ -z "$NO_TEAM_MASTER" ]; then + for i in /tmp/team.*.info; do + [ -e "$i" ] || continue + unset teammaster + unset teamslaves + # shellcheck disable=SC1090 + . "$i" + for testslave in $teamslaves; do + [ "$netif" != "$testslave" ] && continue + + [ -e "/tmp/team.$teammaster.up" ] && exit 0 + + # wait for all slaves to show up + for slave in $teamslaves; do + # try to create the slave (maybe vlan or bridge) + NO_TEAM_MASTER=yes NO_AUTO_DHCP=yes ifup "$slave" + + if ! ip link show dev "$slave" > /dev/null 2>&1; then + # wait for the last slave to show up + exit 0 + fi + done + + if [ ! -e "/tmp/team.$teammaster.up" ]; then + # We shall only bring up those _can_ come up + # in case of some slave is gone in active-backup mode + working_slaves="" + for slave in $teamslaves; do + teamdctl "${teammaster}" port present "${slave}" 2> /dev/null \ + && continue + ip link set dev "$slave" up 2> /dev/null + if wait_for_if_up "$slave"; then + working_slaves="$working_slaves$slave " + fi + done + # Do not add slaves now + teamd -d -U -n -N -t "$teammaster" -f "/etc/teamd/${teammaster}.conf" + for slave in $working_slaves; do + # team requires the slaves to be down before joining team + ip link set dev "$slave" down + ( + unset TEAM_PORT_CONFIG + read -r _hwaddr < "/sys/class/net/$slave/address" + _subchannels=$(iface_get_subchannels "$slave") + if [ -n "$_hwaddr" ] && [ -e "/etc/sysconfig/network-scripts/mac-${_hwaddr}.conf" ]; then + # shellcheck disable=SC1090 + . "/etc/sysconfig/network-scripts/mac-${_hwaddr}.conf" + elif [ -n "$_subchannels" ] && [ -e "/etc/sysconfig/network-scripts/ccw-${_subchannels}.conf" ]; then + # shellcheck disable=SC1090 + . "/etc/sysconfig/network-scripts/ccw-${_subchannels}.conf" + elif [ -e "/etc/sysconfig/network-scripts/ifcfg-${slave}" ]; then + # shellcheck disable=SC1090 + . "/etc/sysconfig/network-scripts/ifcfg-${slave}" + fi + + if [ -n "${TEAM_PORT_CONFIG}" ]; then + /usr/bin/teamdctl "${teammaster}" port config update "${slave}" "${TEAM_PORT_CONFIG}" + fi + ) + teamdctl "$teammaster" port add "$slave" + done + + ip link set dev "$teammaster" up + + : > "/tmp/team.$teammaster.up" + NO_TEAM_MASTER=yes ifup "$teammaster" + exit $? + fi + done + done +fi + +# all synthetic interfaces done.. now check if the interface is available +if ! ip link show dev "$netif" > /dev/null 2>&1; then + exit 1 +fi + +# disable manual ifup while netroot is set for simplifying our logic +# in netroot case we prefer netroot to bringup $netif automatically +[ -n "$2" -a "$2" = "-m" ] && [ -z "$netroot" ] && manualup="$2" + +if [ -n "$manualup" ]; then + : > "/tmp/net.$netif.manualup" + rm -f "/tmp/net.${netif}.did-setup" +else + [ -e "/tmp/net.${netif}.did-setup" ] && exit 0 + [ -z "$DO_VLAN" ] \ + && [ -e "/sys/class/net/$netif/address" ] \ + && [ -e "/tmp/net.$(cat "/sys/class/net/$netif/address").did-setup" ] && exit 0 +fi + +# Specific configuration, spin through the kernel command line +# looking for ip= lines +for p in $(getargs ip=); do + ip_to_var "$p" + # skip ibft + [ "$autoconf" = "ibft" ] && continue + + case "$dev" in + ??:??:??:??:??:??) # MAC address + _dev=$(iface_for_mac "$dev") + [ -n "$_dev" ] && dev="$_dev" + ;; + ??-??-??-??-??-??) # MAC address in BOOTIF form + _dev=$(iface_for_mac "$(fix_bootif "$dev")") + [ -n "$_dev" ] && dev="$_dev" + ;; + esac + + # If this option isn't directed at our interface, skip it + if [ -n "$dev" ]; then + if [ "$dev" != "$netif" ]; then + [ ! -e "/sys/class/net/$dev" ] \ + && warn "Network interface '$dev' does not exist!" + continue + fi + else + iface_is_enslaved "$netif" && continue + fi + + # Store config for later use + for i in ip srv gw mask hostname macaddr mtu dns1 dns2; do + eval '[ "$'$i'" ] && echo '$i'="$'$i'"' + done > "/tmp/net.$netif.override" + + for autoopt in $(str_replace "$autoconf" "," " "); do + case $autoopt in + dhcp | on | any) + do_dhcp -4 + ;; + single-dhcp) + do_dhcp_parallel -4 + exit 0 + ;; + dhcp6) + load_ipv6 + do_dhcp -6 + ;; + auto6) + do_ipv6auto + ;; + either6) + do_ipv6auto || do_dhcp -6 + ;; + link6) + do_ipv6link + ;; + *) + do_static + ;; + esac + done + ret=$? + + # setup nameserver + for s in "$dns1" "$dns2" $(getargs nameserver); do + [ -n "$s" ] || continue + echo "nameserver $s" >> "/tmp/net.$netif.resolv.conf" + done + + if [ $ret -eq 0 ]; then + : > "/tmp/net.${netif}.up" + + if [ -z "$DO_VLAN" ] && [ -e "/sys/class/net/${netif}/address" ]; then + : > "/tmp/net.$(cat "/sys/class/net/${netif}/address").up" + fi + + # and finally, finish interface set up if there isn't already a script + # to do so (which is the case in the dhcp path) + if [ ! -e "$hookdir/initqueue/setup_net_$netif.sh" ]; then + setup_net "$netif" + source_hook initqueue/online "$netif" + if [ -z "$manualup" ]; then + /sbin/netroot "$netif" + fi + fi + + exit $ret + fi +done + +# no ip option directed at our interface? +if [ -z "$NO_AUTO_DHCP" ] && [ ! -e "/tmp/net.${netif}.up" ]; then + ret=1 + if [ -e /tmp/net.bootdev ]; then + read -r BOOTDEV < /tmp/net.bootdev + if [ "$netif" = "$BOOTDEV" ] || [ "$BOOTDEV" = "$(cat "/sys/class/net/${netif}/address")" ]; then + do_dhcp + ret=$? + fi + else + # No ip lines, no bootdev -> default to dhcp + ip=$(getarg ip) + + if getargs 'ip=dhcp6' > /dev/null || [ -z "$ip" -a "$netroot" = "dhcp6" ]; then + load_ipv6 + do_dhcp -6 + ret=$? + fi + if getargs 'ip=dhcp' > /dev/null || [ -z "$ip" -a "$netroot" != "dhcp6" ]; then + do_dhcp -4 + ret=$? + fi + fi + + for s in $(getargs nameserver); do + [ -n "$s" ] || continue + echo "nameserver $s" >> "/tmp/net.$netif.resolv.conf" + done + + if [ "$ret" -eq 0 ] && [ -n "$(ls "/tmp/leaseinfo.${netif}"* 2> /dev/null)" ]; then + : > "/tmp/net.${netif}.did-setup" + if [ -e "/sys/class/net/${netif}/address" ]; then + : > "/tmp/net.$(cat "/sys/class/net/${netif}/address").did-setup" + fi + fi +fi + +exit 0 diff --git a/modules.d/35network-legacy/kill-dhclient.sh b/modules.d/35network-legacy/kill-dhclient.sh new file mode 100755 index 0000000..9ed615f --- /dev/null +++ b/modules.d/35network-legacy/kill-dhclient.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +for f in /tmp/dhclient.*.pid; do + [ -e "$f" ] || continue + read -r PID < "$f" + kill "$PID" > /dev/null 2>&1 +done + +sleep 0.1 + +for f in /tmp/dhclient.*.pid; do + [ -e "$f" ] || continue + read -r PID < "$f" + kill -9 "$PID" > /dev/null 2>&1 +done diff --git a/modules.d/35network-legacy/module-setup.sh b/modules.d/35network-legacy/module-setup.sh new file mode 100755 index 0000000..868ea8f --- /dev/null +++ b/modules.d/35network-legacy/module-setup.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +# called by dracut +check() { + require_binaries ip dhclient sed awk grep pgrep tr expr || return 1 + require_any_binary arping arping2 || return 1 + + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + # arping depends on af_packet + hostonly='' instmods af_packet +} + +# called by dracut +install() { + local _arch + + #Adding default link + if dracut_module_included "systemd"; then + inst_multiple -o "${systemdnetwork}/99-default.link" + [[ $hostonly ]] && inst_multiple -H -o "${systemdnetworkconfdir}/*.link" + fi + + inst_multiple ip dhclient sed awk grep pgrep tr expr + + inst_multiple -o arping arping2 + strstr "$(arping 2>&1)" "ARPing 2" && mv "$initdir/bin/arping" "$initdir/bin/arping2" + + inst_multiple -o ping ping6 + inst_multiple -o teamd teamdctl teamnl + inst_simple /etc/libnl/classid + inst_script "$moddir/ifup.sh" "/sbin/ifup" + inst_script "$moddir/dhcp-multi.sh" "/sbin/dhcp-multi.sh" + inst_script "$moddir/dhclient-script.sh" "/sbin/dhclient-script" + inst_simple -H "/etc/dhclient.conf" + cat "$moddir/dhclient.conf" >> "${initdir}/etc/dhclient.conf" + inst_hook pre-udev 60 "$moddir/net-genrules.sh" + inst_hook cmdline 92 "$moddir/parse-ibft.sh" + inst_hook cmdline 95 "$moddir/parse-vlan.sh" + inst_hook cmdline 96 "$moddir/parse-bond.sh" + inst_hook cmdline 96 "$moddir/parse-team.sh" + inst_hook cmdline 97 "$moddir/parse-bridge.sh" + inst_hook cmdline 98 "$moddir/parse-ip-opts.sh" + inst_hook cmdline 99 "$moddir/parse-ifname.sh" + inst_hook cleanup 10 "$moddir/kill-dhclient.sh" + + # install all config files for teaming + unset TEAM_MASTER + unset TEAM_CONFIG + unset TEAM_PORT_CONFIG + unset HWADDR + unset SUBCHANNELS + for i in /etc/sysconfig/network-scripts/ifcfg-*; do + [ -e "$i" ] || continue + case "$i" in + *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave) + continue + ;; + esac + ( + # shellcheck disable=SC1090 + . "$i" + if ! [ "${ONBOOT}" = "no" -o "${ONBOOT}" = "NO" ] \ + && [ -n "${TEAM_MASTER}${TEAM_CONFIG}${TEAM_PORT_CONFIG}" ]; then + if [ -n "$TEAM_CONFIG" ] && [ -n "$DEVICE" ]; then + mkdir -p "$initdir"/etc/teamd + printf -- "%s" "$TEAM_CONFIG" > "$initdir/etc/teamd/${DEVICE}.conf" + elif [ -n "$TEAM_PORT_CONFIG" ]; then + inst_simple "$i" + + HWADDR="$(echo "$HWADDR" | sed 'y/ABCDEF/abcdef/')" + if [ -n "$HWADDR" ]; then + ln_r "$i" "/etc/sysconfig/network-scripts/mac-${HWADDR}.conf" + fi + + SUBCHANNELS="$(echo "$SUBCHANNELS" | sed 'y/ABCDEF/abcdef/')" + if [ -n "$SUBCHANNELS" ]; then + ln_r "$i" "/etc/sysconfig/network-scripts/ccw-${SUBCHANNELS}.conf" + fi + fi + fi + ) + done + + _arch=${DRACUT_ARCH:-$(uname -m)} + + inst_libdir_file {"tls/$_arch/",tls/,"$_arch/",}"libnss_dns.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libnss_mdns4_minimal.so.*" + + dracut_need_initqueue +} diff --git a/modules.d/35network-legacy/net-genrules.sh b/modules.d/35network-legacy/net-genrules.sh new file mode 100755 index 0000000..686db59 --- /dev/null +++ b/modules.d/35network-legacy/net-genrules.sh @@ -0,0 +1,125 @@ +#!/bin/sh + +getargbool 0 rd.neednet && NEEDNET=1 + +# Don't continue if we don't need network +if [ -z "$netroot" ] && [ ! -e "/tmp/net.ifaces" ] && [ "$NEEDNET" != "1" ]; then + return +fi + +command -v fix_bootif > /dev/null || . /lib/net-lib.sh + +# Write udev rules +{ + # bridge: attempt only the defined interface + for i in /tmp/bridge.*.info; do + [ -e "$i" ] || continue + unset bridgeslaves + unset bridgename + # shellcheck disable=SC1090 + . "$i" + RAW_IFACES="$RAW_IFACES $bridgeslaves" + MASTER_IFACES="$MASTER_IFACES $bridgename" + done + + # bond: attempt only the defined interface (override bridge defines) + for i in /tmp/bond.*.info; do + [ -e "$i" ] || continue + unset bondslaves + unset bondname + # shellcheck disable=SC1090 + . "$i" + # It is enough to fire up only one + RAW_IFACES="$RAW_IFACES $bondslaves" + MASTER_IFACES="$MASTER_IFACES ${bondname}" + done + + for i in /tmp/team.*.info; do + [ -e "$i" ] || continue + unset teamslaves + unset teammaster + # shellcheck disable=SC1090 + . "$i" + RAW_IFACES="$RAW_IFACES ${teamslaves}" + MASTER_IFACES="$MASTER_IFACES ${teammaster}" + done + + for i in /tmp/vlan.*.phy; do + [ -e "$i" ] || continue + unset phydevice + # shellcheck disable=SC1090 + . "$i" + RAW_IFACES="$RAW_IFACES $phydevice" + for j in /tmp/vlan.*".${phydevice}"; do + [ -e "$j" ] || continue + unset vlanname + # shellcheck disable=SC1090 + . "$j" + MASTER_IFACES="$MASTER_IFACES ${vlanname}" + done + done + + MASTER_IFACES="$(trim "$MASTER_IFACES")" + RAW_IFACES="$(trim "$RAW_IFACES")" + + if [ -z "$IFACES" ]; then + [ -e /tmp/net.ifaces ] && read -r IFACES < /tmp/net.ifaces + fi + + if [ -e /tmp/net.bootdev ]; then + read -r bootdev < /tmp/net.bootdev + fi + + # shellcheck disable=SC2016 + ifup='/sbin/ifup $name' + + runcmd="RUN+=\"/sbin/initqueue --name ifup-\$name --unique --onetime $ifup\"" + + # We have some specific interfaces to handle + if [ -n "${RAW_IFACES}${IFACES}" ]; then + echo 'SUBSYSTEM!="net", GOTO="net_end"' + echo 'ACTION!="add|change|move", GOTO="net_end"' + for iface in $IFACES $RAW_IFACES; do + case "$iface" in + ??:??:??:??:??:??) # MAC address + cond="ATTR{address}==\"$iface\"" + echo "$cond, $runcmd, GOTO=\"net_end\"" + ;; + ??-??-??-??-??-??) # MAC address in BOOTIF form + cond="ATTR{address}==\"$(fix_bootif "$iface")\"" + echo "$cond, $runcmd, GOTO=\"net_end\"" + ;; + *) # an interface name + cond="ENV{INTERFACE}==\"$iface\"" + echo "$cond, $runcmd, GOTO=\"net_end\"" + cond="NAME==\"$iface\"" + echo "$cond, $runcmd, GOTO=\"net_end\"" + ;; + esac + # The GOTO prevents us from trying to ifup the same device twice + done + echo 'LABEL="net_end"' + + for iface in $IFACES; do + if [ "$bootdev" = "$iface" ] || [ "$NEEDNET" = "1" ]; then + if [ -n "$netroot" ] && [ -n "$DRACUT_SYSTEMD" ]; then + echo "systemctl is-active initrd-root-device.target || [ -f /tmp/net.${iface}.did-setup ]" + else + echo "[ -f /tmp/net.${iface}.did-setup ]" + fi > "$hookdir"/initqueue/finished/wait-"$iface".sh + fi + done + # Default: We don't know the interface to use, handle all + # Fixme: waiting for the interface as well. + else + cond='ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}!="wlan|wwan"' + # if you change the name of "91-default-net.rules", also change modules.d/80cms/cmssetup.sh + echo "$cond, $runcmd" > /etc/udev/rules.d/91-default-net.rules + if [ "$NEEDNET" = "1" ]; then + # shellcheck disable=SC2016 + echo 'for i in /tmp/net.*.did-setup; do [ -f "$i" ] && exit 0; done; exit 1' > "$hookdir"/initqueue/finished/wait-network.sh + fi + fi + + # if you change the name of "90-net.rules", also change modules.d/80cms/cmssetup.sh +} > /etc/udev/rules.d/90-net.rules diff --git a/modules.d/35network-legacy/parse-bond.sh b/modules.d/35network-legacy/parse-bond.sh new file mode 100755 index 0000000..ba30a3b --- /dev/null +++ b/modules.d/35network-legacy/parse-bond.sh @@ -0,0 +1,76 @@ +#!/bin/sh +# +# Format: +# bond=<bondname>[:<bondslaves>[:<options>[:<mtu>]]] +# +# bondslaves is a comma-separated list of physical (ethernet) interfaces +# options is a comma-separated list on bonding options (modinfo bonding for details) in format compatible with initscripts +# if options include multi-valued arp_ip_target option, then its values should be separated by semicolon. +# +# bond without parameters assumes bond=bond0:eth0,eth1:mode=balance-rr +# +# if the mtu is specified, it will be set on the bond master +# + +# We translate list of slaves to space-separated here to make it easier to loop over them in ifup +# Ditto for bonding options +parsebond() { + local v="${1}": + set -- + while [ -n "$v" ]; do + set -- "$@" "${v%%:*}" + v=${v#*:} + done + + case $# in + 0) + bondname=bond0 + bondslaves="eth0 eth1" + ;; + 1) + bondname=$1 + bondslaves="eth0 eth1" + ;; + 2) + bondname=$1 + bondslaves=$(str_replace "$2" "," " ") + ;; + 3) + bondname=$1 + bondslaves=$(str_replace "$2" "," " ") + bondoptions=$(str_replace "$3" "," " ") + ;; + 4) + bondname=$1 + bondslaves=$(str_replace "$2" "," " ") + bondoptions=$(str_replace "$3" "," " ") + bondmtu=$4 + ;; + *) die "bond= requires zero to four parameters" ;; + esac +} + +# Parse bond for bondname, bondslaves, bondmode, bondoptions and bondmtu +for bond in $(getargs bond=); do + unset bondname + unset bondslaves + unset bondoptions + unset bondmtu + if [ "$bond" != "bond" ]; then + parsebond "$bond" + fi + # Simple default bond + if [ -z "$bondname" ]; then + bondname=bond0 + bondslaves="eth0 eth1" + fi + # Make it suitable for initscripts export + bondoptions=$(str_replace "$bondoptions" ";" ",") + + { + echo "bondname=$bondname" + echo "bondslaves=\"$bondslaves\"" + echo "bondoptions=\"$bondoptions\"" + echo "bondmtu=\"$bondmtu\"" + } > "/tmp/bond.${bondname}.info" +done diff --git a/modules.d/35network-legacy/parse-bridge.sh b/modules.d/35network-legacy/parse-bridge.sh new file mode 100755 index 0000000..caea1da --- /dev/null +++ b/modules.d/35network-legacy/parse-bridge.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# +# Format: +# bridge=<bridgename>:<bridgeslaves> +# +# <bridgeslaves> is a comma-separated list of physical (ethernet) interfaces +# bridge without parameters assumes bridge=br0:eth0 +# + +parsebridge() { + local v="${1}": + set -- + while [ -n "$v" ]; do + set -- "$@" "${v%%:*}" + v=${v#*:} + done + case $# in + 0) + bridgename=br0 + bridgeslaves=$iface + ;; + 1) die "bridge= requires two parameters" ;; + 2) + bridgename=$1 + bridgeslaves=$(str_replace "$2" "," " ") + ;; + *) die "bridge= requires two parameters" ;; + esac +} + +# Parse bridge for bridgename and bridgeslaves +for bridge in $(getargs bridge=); do + unset bridgename + unset bridgeslaves + iface=eth0 + # Read bridge= parameters if they exist + if [ "$bridge" != "bridge" ]; then + parsebridge "$bridge" + fi + # Simple default bridge + if [ -z "$bridgename" ]; then + bridgename=br0 + bridgeslaves=$iface + fi + { + echo "bridgename=$bridgename" + echo "bridgeslaves=\"$bridgeslaves\"" + } > /tmp/bridge.${bridgename}.info +done diff --git a/modules.d/35network-legacy/parse-ibft.sh b/modules.d/35network-legacy/parse-ibft.sh new file mode 100755 index 0000000..1937f13 --- /dev/null +++ b/modules.d/35network-legacy/parse-ibft.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +command -v getarg > /dev/null || . /lib/dracut-lib.sh +command -v ibft_to_cmdline > /dev/null || . /lib/net-lib.sh + +if getargbool 0 rd.iscsi.ibft -d "ip=ibft"; then + modprobe -b -q iscsi_boot_sysfs 2> /dev/null + modprobe -b -q iscsi_ibft + ibft_to_cmdline +fi diff --git a/modules.d/35network-legacy/parse-ifname.sh b/modules.d/35network-legacy/parse-ifname.sh new file mode 100755 index 0000000..be7b6ad --- /dev/null +++ b/modules.d/35network-legacy/parse-ifname.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Format: +# ifname=<interface>:<mac> +# +# Note letters in the macaddress must be lowercase! +# +# Examples: +# ifname=eth0:4a:3f:4c:04:f8:d7 +# +# Note when using ifname= to get persistent interface names, you must specify +# an ifname= argument for each interface used in an ip= or fcoe= argument + +# check if there are any ifname parameters +if ! getarg ifname= > /dev/null; then + return +fi + +command -v parse_ifname_opts > /dev/null || . /lib/net-lib.sh + +# Check ifname= lines +for p in $(getargs ifname=); do + parse_ifname_opts "$p" +done diff --git a/modules.d/35network-legacy/parse-ip-opts.sh b/modules.d/35network-legacy/parse-ip-opts.sh new file mode 100755 index 0000000..8263321 --- /dev/null +++ b/modules.d/35network-legacy/parse-ip-opts.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# +# Format: +# ip=[dhcp|on|any|single-dhcp] +# +# ip=<interface>:[dhcp|on|any][:[<mtu>][:<macaddr>]] +# +# ip=<client-IP-number>:<server-IP-number>:<gateway-IP-number>:<netmask>:<client-hostname>:<interface>:{dhcp|on|any|none|off}[:[<mtu>][:<macaddr>]] +# +# When supplying more than only ip= line, <interface> is mandatory and +# bootdev= must contain the name of the primary interface to use for +# routing,dns,dhcp-options,etc. +# + +# we really need to use `expr substr` with dash +# shellcheck disable=SC2003 disable=SC2308 + +command -v getarg > /dev/null || . /lib/dracut-lib.sh + +if [ -n "$netroot" ] && [ -z "$(getarg ip=)" ] && [ -z "$(getarg BOOTIF=)" ]; then + # No ip= argument(s) for netroot provided, defaulting to DHCP + return +fi + +# Count ip= lines to decide whether we need bootdev= or not +if [ -z "$NEEDBOOTDEV" ]; then + count=0 + for p in $(getargs ip=); do + case "$p" in + ibft) + continue + ;; + esac + count=$((count + 1)) + done + [ $count -gt 1 ] && NEEDBOOTDEV=1 +fi +unset count + +# If needed, check if bootdev= contains anything usable +BOOTDEV=$(getarg bootdev=) + +if [ -n "$NEEDBOOTDEV" ] && getargbool 1 rd.neednet; then + #[ -z "$BOOTDEV" ] && warn "Please supply bootdev argument for multiple ip= lines" + echo "rd.neednet=1" > /etc/cmdline.d/dracut-neednet.conf + info "Multiple ip= arguments: assuming rd.neednet=1" +else + unset NEEDBOOTDEV +fi + +# Check ip= lines +# XXX Would be nice if we could errorcheck ip addresses here as well +for p in $(getargs ip=); do + ip_to_var "$p" + + # make first device specified the BOOTDEV + if [ -n "$NEEDBOOTDEV" ] && [ -z "$BOOTDEV" ] && [ -n "$dev" ]; then + BOOTDEV="$dev" + info "Setting bootdev to '$BOOTDEV'" + fi + + # skip ibft since we did it above + [ "$autoconf" = "ibft" ] && continue + + # Empty autoconf defaults to 'dhcp' + if [ -z "$autoconf" ]; then + warn "Empty autoconf values default to dhcp" + autoconf="dhcp" + fi + + # Error checking for autoconf in combination with other values + for autoopt in $(str_replace "$autoconf" "," " "); do + case $autoopt in + error) die "Error parsing option 'ip=$p'" ;; + bootp | rarp | both) die "Sorry, ip=$autoopt is currently unsupported" ;; + none | off) + [ -z "$ip" ] \ + && die "For argument 'ip=$p'\nValue '$autoopt' without static configuration does not make sense" + [ -z "$mask" ] \ + && die "Sorry, automatic calculation of netmask is not yet supported" + ;; + auto6 | link6) ;; + either6) ;; + dhcp | dhcp6 | on | any | single-dhcp) + [ -n "$NEEDBOOTDEV" ] && [ -z "$dev" ] \ + && die "Sorry, 'ip=$p' does not make sense for multiple interface configurations" + [ -n "$ip" ] \ + && die "For argument 'ip=$p'\nSorry, setting client-ip does not make sense for '$autoopt'" + ;; + *) die "For argument 'ip=$p'\nSorry, unknown value '$autoopt'" ;; + esac + done + + if [ -n "$dev" ]; then + # We don't like duplicate device configs + if [ -n "$IFACES" ]; then + for i in $IFACES; do + [ "$dev" = "$i" ] && die "For argument 'ip=$p'\nDuplication configurations for '$dev'" + done + fi + # IFACES list for later use + IFACES="$IFACES $dev" + + # Interface should exist + if [ ! -e "/sys/class/net/$dev" ]; then + warn "Network interface '$dev' does not exist" + fi + fi + + # Do we need to check for specific options? + if [ -n "$NEEDDHCP" ] || [ -n "$DHCPORSERVER" ]; then + # Correct device? (Empty is ok as well) + [ "$dev" = "$BOOTDEV" ] || continue + # Server-ip is there? + [ -n "$DHCPORSERVER" ] && [ -n "$srv" ] && continue + # dhcp? (It's simpler to check for a set ip. Checks above ensure that if + # ip is there, we're static + [ -z "$ip" ] && continue + # Not good! + die "Server-ip or dhcp for netboot needed, but current arguments say otherwise" + fi + + if str_starts "$dev" "enx" && [ ${#dev} -eq 15 ]; then + # shellcheck disable=SC2003 + printf -- "ifname=%s:%s:%s:%s:%s:%s:%s\n" \ + "$dev" \ + "$(expr substr "$dev" 3 2)" \ + "$(expr substr "$dev" 5 2)" \ + "$(expr substr "$dev" 7 2)" \ + "$(expr substr "$dev" 9 2)" \ + "$(expr substr "$dev" 11 2)" \ + "$(expr substr "$dev" 13 2)" \ + >> /etc/cmdline.d/80-enx.conf + fi +done + +# put BOOTIF in IFACES to make sure it comes up +if getargbool 1 "rd.bootif" && BOOTIF="$(getarg BOOTIF=)"; then + BOOTDEV=$(fix_bootif "$BOOTIF") + IFACES="$BOOTDEV $IFACES" +fi + +# This ensures that BOOTDEV is always first in IFACES +if [ -n "$BOOTDEV" ] && [ -n "$IFACES" ]; then + IFACES="${IFACES%"$BOOTDEV"*} ${IFACES#*"$BOOTDEV"}" + IFACES="$BOOTDEV $IFACES" +fi + +# Store BOOTDEV and IFACES for later use +[ -n "$BOOTDEV" ] && echo "$BOOTDEV" > /tmp/net.bootdev +[ -n "$IFACES" ] && echo "$IFACES" > /tmp/net.ifaces diff --git a/modules.d/35network-legacy/parse-team.sh b/modules.d/35network-legacy/parse-team.sh new file mode 100755 index 0000000..83badc9 --- /dev/null +++ b/modules.d/35network-legacy/parse-team.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# +# Format: +# team=<teammaster>:<teamslaves>[:<teamrunner>] +# +# teamslaves is a comma-separated list of physical (ethernet) interfaces +# teamrunner is the runner type to be used (see teamd.conf(5)); defaults to activebackup +# +# team without parameters assumes team=team0:eth0,eth1:activebackup +# + +parseteam() { + local v="${1}": + set -- + while [ -n "$v" ]; do + set -- "$@" "${v%%:*}" + v=${v#*:} + done + + case $# in + 0) + teammaster=team0 + teamslaves="eth0 eth1" + teamrunner="activebackup" + ;; + 1) + teammaster=$1 + teamslaves="eth0 eth1" + teamrunner="activebackup" + ;; + 2) + teammaster=$1 + teamslaves=$(str_replace "$2" "," " ") + teamrunner="activebackup" + ;; + 3) + teammaster=$1 + teamslaves=$(str_replace "$2" "," " ") + teamrunner=$3 + ;; + *) die "team= requires zero to three parameters" ;; + esac + return 0 +} + +for team in $(getargs team); do + [ "$team" = "team" ] && continue + + unset teammaster + unset teamslaves + unset teamrunner + + parseteam "$team" || continue + + { + echo "teammaster=$teammaster" + echo "teamslaves=\"$teamslaves\"" + echo "teamrunner=\"$teamrunner\"" + } > /tmp/team."${teammaster}".info + + if ! [ -e /etc/teamd/"${teammaster}".conf ]; then + warn "Team master $teammaster specified, but no /etc/teamd/$teammaster.conf present. Using $teamrunner." + mkdir -p /etc/teamd + printf -- "%s" "{\"runner\": {\"name\": \"$teamrunner\"}, \"link_watch\": {\"name\": \"ethtool\"}}" > "/tmp/${teammaster}.conf" + fi +done diff --git a/modules.d/35network-legacy/parse-vlan.sh b/modules.d/35network-legacy/parse-vlan.sh new file mode 100755 index 0000000..c23f833 --- /dev/null +++ b/modules.d/35network-legacy/parse-vlan.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Format: +# vlan=<vlanname>:<phydevice> +# + +parsevlan() { + local v="${1}": + set -- + while [ -n "$v" ]; do + set -- "$@" "${v%%:*}" + v=${v#*:} + done + + unset vlanname phydevice + case $# in + 2) + vlanname=$1 + phydevice=$2 + ;; + *) die "vlan= requires two parameters" ;; + esac +} + +for vlan in $(getargs vlan=); do + unset vlanname + unset phydevice + if [ ! "$vlan" = "vlan" ]; then + parsevlan "$vlan" + fi + + echo "phydevice=\"$phydevice\"" > /tmp/vlan."${phydevice}".phy + { + echo "vlanname=\"$vlanname\"" + echo "phydevice=\"$phydevice\"" + } > /tmp/vlan."${vlanname}"."${phydevice}" +done diff --git a/modules.d/35network-manager/initrd-no-auto-default.conf b/modules.d/35network-manager/initrd-no-auto-default.conf new file mode 100644 index 0000000..8a06e52 --- /dev/null +++ b/modules.d/35network-manager/initrd-no-auto-default.conf @@ -0,0 +1,5 @@ +[.config] +enable=env:initrd + +[main] +no-auto-default=* diff --git a/modules.d/35network-manager/module-setup.sh b/modules.d/35network-manager/module-setup.sh new file mode 100755 index 0000000..d9a244a --- /dev/null +++ b/modules.d/35network-manager/module-setup.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +# called by dracut +check() { + require_binaries sed grep NetworkManager || return 1 + + # do not add this module by default + return 255 +} + +# called by dracut +depends() { + echo dbus bash + return 0 +} + +# called by dracut +installkernel() { + return 0 +} + +# called by dracut +install() { + local _nm_version + + _nm_version=${NM_VERSION:-$(NetworkManager --version)} + + # We don't need `ip` but having it is *really* useful for people debugging + # in an emergency shell. + inst_multiple ip sed grep + + inst NetworkManager + inst_multiple -o /usr/{lib,libexec}/nm-initrd-generator + inst_multiple -o /usr/{lib,libexec}/nm-daemon-helper + inst_multiple -o teamd dhclient + inst_hook cmdline 99 "$moddir/nm-config.sh" + if dracut_module_included "systemd"; then + + inst "$dbussystem"/org.freedesktop.NetworkManager.conf + inst_multiple nmcli nm-online + + # teaming support under systemd+dbus + inst_multiple -o \ + "$dbussystem"/teamd.conf \ + "$dbussystemconfdir"/teamd.conf + + # Install a configuration snippet to prevent the automatic creation of + # "Wired connection #" DHCP connections for Ethernet interfaces + inst_simple "$moddir"/initrd-no-auto-default.conf /usr/lib/NetworkManager/conf.d/ + + inst_simple "$moddir"/nm-initrd.service "$systemdsystemunitdir"/nm-initrd.service + inst_simple "$moddir"/nm-wait-online-initrd.service "$systemdsystemunitdir"/nm-wait-online-initrd.service + + # Adding default link + inst_multiple -o "${systemdnetwork}/99-default.link" + [[ $hostonly ]] && inst_multiple -H -o "${systemdnetworkconfdir}/*.link" + + $SYSTEMCTL -q --root "$initdir" enable nm-initrd.service + fi + + inst_hook initqueue/settled 99 "$moddir/nm-run.sh" + + inst_rules 85-nm-unmanaged.rules + inst_libdir_dir "NetworkManager/$_nm_version" + inst_libdir_file "NetworkManager/$_nm_version/libnm-device-plugin-team.so" + inst_simple "$moddir/nm-lib.sh" "/lib/nm-lib.sh" + + if [[ -x "$initdir/usr/sbin/dhclient" ]]; then + inst_multiple -o /usr/{lib,libexec}/nm-dhcp-helper + elif ! [[ -e "$initdir/etc/machine-id" ]]; then + # The internal DHCP client silently fails if we + # have no machine-id + systemd-machine-id-setup --root="$initdir" + fi + + # We don't install the ifcfg files from the host automatically. + # But the user might choose to include them, so we pull in the machinery to read them. + inst_libdir_file "NetworkManager/$_nm_version/libnm-settings-plugin-ifcfg-rh.so" + + _arch=${DRACUT_ARCH:-$(uname -m)} + + inst_libdir_file {"tls/$_arch/",tls/,"$_arch/",}"libnss_dns.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libnss_mdns4_minimal.so.*" +} diff --git a/modules.d/35network-manager/nm-config.sh b/modules.d/35network-manager/nm-config.sh new file mode 100755 index 0000000..2b13d0a --- /dev/null +++ b/modules.d/35network-manager/nm-config.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +type nm_generate_connections > /dev/null 2>&1 || . /lib/nm-lib.sh + +if [ -n "$netroot" ] || [ -e /tmp/net.ifaces ]; then + echo rd.neednet >> /etc/cmdline.d/35-neednet.conf +fi + +if getargbool 0 rd.debug -d -y rdinitdebug -d -y rdnetdebug; then + # shellcheck disable=SC2174 + mkdir -m 0755 -p /run/NetworkManager/conf.d + ( + echo '[.config]' + echo 'enable=env:initrd' + echo + echo '[logging]' + echo 'level=TRACE' + ) > /run/NetworkManager/conf.d/initrd-logging.conf + + if [ -n "$DRACUT_SYSTEMD" ]; then + # Enable tty output if a usable console is found + # See https://github.com/coreos/fedora-coreos-tracker/issues/943 + # shellcheck disable=SC2217 + if [ -w /dev/console ] && (echo < /dev/console) > /dev/null 2> /dev/null; then + mkdir -p /run/systemd/system/nm-initrd.service.d + cat << EOF > /run/systemd/system/nm-initrd.service.d/tty-output.conf +[Service] +StandardOutput=tty +EOF + systemctl --no-block daemon-reload + fi + fi +fi + +nm_generate_connections diff --git a/modules.d/35network-manager/nm-initrd.service b/modules.d/35network-manager/nm-initrd.service new file mode 100644 index 0000000..dbd8caa --- /dev/null +++ b/modules.d/35network-manager/nm-initrd.service @@ -0,0 +1,31 @@ +[Unit] +DefaultDependencies=no +Wants=systemd-udev-trigger.service +After=systemd-udev-trigger.service +After=dracut-cmdline.service +After=dbus.service +Wants=network.target +Before=network.target +ConditionPathExists=/run/NetworkManager/initrd/neednet +ConditionPathExistsGlob=|/usr/lib/NetworkManager/system-connections/* +ConditionPathExistsGlob=|/run/NetworkManager/system-connections/* +ConditionPathExistsGlob=|/etc/NetworkManager/system-connections/* +ConditionPathExistsGlob=|/etc/sysconfig/network-scripts/ifcfg-* + +[Service] +Type=dbus +BusName=org.freedesktop.NetworkManager +ExecReload=/usr/bin/busctl call org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.NetworkManager Reload u 0 +ExecStart=/usr/sbin/NetworkManager --debug +KillMode=process +# The following gets changed to StandardOutput=tty by nm-config.sh +# when debug is enabled and a usable console is found. +StandardOutput=null +Environment=NM_CONFIG_ENABLE_TAG=initrd +Restart=on-failure +ProtectSystem=true +ProtectHome=read-only + +[Install] +WantedBy=initrd.target +Also=nm-wait-online-initrd.service diff --git a/modules.d/35network-manager/nm-lib.sh b/modules.d/35network-manager/nm-lib.sh new file mode 100755 index 0000000..32a288d --- /dev/null +++ b/modules.d/35network-manager/nm-lib.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +type getcmdline > /dev/null 2>&1 || . /lib/dracut-lib.sh + +nm_generate_connections() { + rm -f /run/NetworkManager/system-connections/* + if [ -x /usr/libexec/nm-initrd-generator ]; then + # shellcheck disable=SC2046 + /usr/libexec/nm-initrd-generator -- $(getcmdline) + elif [ -x /usr/lib/nm-initrd-generator ]; then + # shellcheck disable=SC2046 + /usr/lib/nm-initrd-generator -- $(getcmdline) + else + warn "nm-initrd-generator not found" + fi + + if getargbool 0 rd.neednet; then + for i in /usr/lib/NetworkManager/system-connections/* \ + /run/NetworkManager/system-connections/* \ + /etc/NetworkManager/system-connections/* \ + /etc/sysconfig/network-scripts/ifcfg-*; do + [ -f "$i" ] || continue + mkdir -p "$hookdir"/initqueue/finished + echo '[ -f /tmp/nm.done ]' > "$hookdir"/initqueue/finished/nm.sh + mkdir -p /run/NetworkManager/initrd + : > /run/NetworkManager/initrd/neednet # activate NM services + break + done + fi +} + +nm_reload_connections() { + [ -n "$DRACUT_SYSTEMD" ] && systemctl is-active nm-initrd.service && nmcli connection reload +} diff --git a/modules.d/35network-manager/nm-run.sh b/modules.d/35network-manager/nm-run.sh new file mode 100755 index 0000000..14b9cb1 --- /dev/null +++ b/modules.d/35network-manager/nm-run.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +type source_hook > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if [ -z "$DRACUT_SYSTEMD" ]; then + # Only start NM if networking is needed + if [ -e /run/NetworkManager/initrd/neednet ]; then + for i in /usr/lib/NetworkManager/system-connections/* \ + /run/NetworkManager/system-connections/* \ + /etc/NetworkManager/system-connections/* \ + /etc/sysconfig/network-scripts/ifcfg-*; do + [ -f "$i" ] || continue + /usr/sbin/NetworkManager --configure-and-quit=initrd --no-daemon + break + done + fi +fi + +if [ -s /run/NetworkManager/initrd/hostname ]; then + cat /run/NetworkManager/initrd/hostname > /proc/sys/kernel/hostname +fi + +kf_get_string() { + # NetworkManager writes keyfiles (glib's GKeyFile API). Have a naive + # parser for it. + # + # But GKeyFile will backslash escape certain keys (\s, \t, \n) but also + # escape backslash. As an approximation, interpret the string with printf's + # '%b'. + # + # This is supposed to mimic g_key_file_get_string() (poorly). + + v1="$(sed -n "s/^$1=/=/p" | sed '1!d')" + test "$v1" = "${v1#=}" && return 1 + printf "%b" "${v1#=}" +} + +kf_unescape() { + # Another layer of unescaping. While values in GKeyFile format + # are backslash escaped, the original strings (which are in no + # defined encoding) are backslash escaped too to be valid UTF-8. + # This will undo the second layer of escaping to give binary "strings". + printf "%b" "$1" +} + +kf_parse() { + v3="$(kf_get_string "$1")" || return 1 + v3="$(kf_unescape "$v3")" + printf '%s=%s\n' "$2" "$(printf '%q' "$v3")" +} + +dhcpopts_create() { + kf_parse root-path new_root_path < "$1" + kf_parse next-server new_next_server < "$1" + kf_parse dhcp-bootfile filename < "$1" +} + +for _i in /sys/class/net/*; do + [ -d "$_i" ] || continue + state="/run/NetworkManager/devices/$(cat "$_i"/ifindex)" + grep -q '^connection-uuid=' "$state" 2> /dev/null || continue + ifname="${_i##*/}" + dhcpopts_create "$state" > /tmp/dhclient."$ifname".dhcpopts + source_hook initqueue/online "$ifname" + /sbin/netroot "$ifname" +done + +: > /tmp/nm.done diff --git a/modules.d/35network-manager/nm-wait-online-initrd.service b/modules.d/35network-manager/nm-wait-online-initrd.service new file mode 100644 index 0000000..ac8762a --- /dev/null +++ b/modules.d/35network-manager/nm-wait-online-initrd.service @@ -0,0 +1,16 @@ +[Unit] +DefaultDependencies=no +Requires=nm-initrd.service +After=nm-initrd.service +Before=network-online.target +Before=dracut-initqueue.service +ConditionPathExists=/run/NetworkManager/initrd/neednet + +[Service] +Type=oneshot +ExecStart=/usr/bin/nm-online -s -q -t 3600 +RemainAfterExit=yes + +[Install] +WantedBy=initrd.target +WantedBy=network-online.target diff --git a/modules.d/40network/dhcp-root.sh b/modules.d/40network/dhcp-root.sh new file mode 100755 index 0000000..3f11221 --- /dev/null +++ b/modules.d/40network/dhcp-root.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# This script is sourced, so root should be set. But let's be paranoid +[ -z "$root" ] && root=$(getarg root=) + +if [ -z "$netroot" ]; then + for netroot in $(getargs netroot=); do + [ "$netroot" = "dhcp" ] && break + [ "$netroot" = "dhcp6" ] && break + done + [ "$netroot" = "dhcp" ] || [ "$netroot" = "dhcp6" ] || unset netroot +fi + +if [ "$root" = "dhcp" ] || [ "$root" = "dhcp6" ] || [ "$netroot" = "dhcp" ] || [ "$netroot" = "dhcp6" ]; then + # Tell ip= checker that we need dhcp + # shellcheck disable=SC2034 + NEEDDHCP="1" + + # Done, all good! + # shellcheck disable=SC2034 + rootok=1 + if [ "$netroot" != "dhcp" ] && [ "$netroot" != "dhcp6" ]; then + netroot=$root + fi + + # Shut up init error check + [ -z "$root" ] && root="dhcp" + # shellcheck disable=SC2016 + echo '[ -d $NEWROOT/proc -o -e /dev/root ]' > "$hookdir"/initqueue/finished/dhcp.sh +fi diff --git a/modules.d/40network/ifname-genrules.sh b/modules.d/40network/ifname-genrules.sh new file mode 100755 index 0000000..b9b95c4 --- /dev/null +++ b/modules.d/40network/ifname-genrules.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# if there are no ifname parameters, just use NAME=KERNEL +if ! getarg ifname= > /dev/null; then + return +fi + +command -v parse_ifname_opts > /dev/null || . /lib/net-lib.sh + +{ + for p in $(getargs ifname=); do + parse_ifname_opts "$p" + + if [ -f /tmp/ifname-"$ifname_mac" ]; then + read -r oldif < /tmp/ifname-"$ifname_mac" + fi + if [ -f /tmp/ifname-"$ifname_if" ]; then + read -r oldmac < /tmp/ifname-"$ifname_if" + fi + if [ -n "$oldif" -a -n "$oldmac" -a "$oldif" = "$ifname_if" -a "$oldmac" = "$ifname_mac" ]; then + # skip same ifname= declaration + continue + fi + + [ -n "$oldif" ] && warn "Multiple interface names specified for MAC $ifname_mac: $oldif" + [ -n "$oldmac" ] && warn "Multiple MAC specified for $ifname_if: $oldmac" + + printf 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="%s", ATTR{type}=="1", NAME="%s"\n' "$ifname_mac" "$ifname_if" + echo "$ifname_if" > /tmp/ifname-"$ifname_mac" + echo "$ifname_mac" > /tmp/ifname-"$ifname_if" + done +} >> /etc/udev/rules.d/80-ifname.rules diff --git a/modules.d/40network/module-setup.sh b/modules.d/40network/module-setup.sh new file mode 100755 index 0000000..4c77ff3 --- /dev/null +++ b/modules.d/40network/module-setup.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + is_qemu_virtualized && echo -n "qemu-net " + + for module in connman network-manager network-legacy systemd-networkd; do + if dracut_module_included "$module"; then + network_handler="$module" + break + fi + done + + if [ -z "$network_handler" ]; then + if check_module "connman"; then + network_handler="connman" + elif check_module "network-manager"; then + network_handler="network-manager" + elif check_module "systemd-networkd"; then + network_handler="systemd-networkd" + else + network_handler="network-legacy" + fi + fi + echo "kernel-network-modules $network_handler" + return 0 +} + +# called by dracut +installkernel() { + return 0 +} + +# called by dracut +install() { + inst_script "$moddir/netroot.sh" "/sbin/netroot" + inst_simple "$moddir/net-lib.sh" "/lib/net-lib.sh" + inst_hook pre-udev 50 "$moddir/ifname-genrules.sh" + inst_hook cmdline 91 "$moddir/dhcp-root.sh" + inst_multiple ip sed awk grep pgrep tr + inst_multiple -o arping arping2 + dracut_need_initqueue +} diff --git a/modules.d/40network/net-lib.sh b/modules.d/40network/net-lib.sh new file mode 100755 index 0000000..9d88e0d --- /dev/null +++ b/modules.d/40network/net-lib.sh @@ -0,0 +1,906 @@ +#!/bin/sh + +# shellcheck disable=SC2034 +IFNETFILE="/tmp/bootnetif" + +is_ip() { + echo "$1" | { + IFS=. read -r a b c d + test "$a" -ge 0 -a "$a" -le 255 \ + -a "$b" -ge 0 -a "$b" -le 255 \ + -a "$c" -ge 0 -a "$c" -le 255 \ + -a "$d" -ge 0 -a "$d" -le 255 \ + 2> /dev/null + } && return 0 + return 1 +} + +get_ip() { + local iface="$1" ip="" + ip=$(ip -f inet addr show "$iface") + ip=${ip%%/*} + ip=${ip##* } + echo "$ip" +} + +iface_for_remote_addr() { + # shellcheck disable=SC2046 + set -- $(ip route get to "$@" | sed 's/.*\bdev\b//p;q') + echo "$1" +} + +iface_for_ip() { + # shellcheck disable=SC2046 + set -- $(ip addr show to "$@") + echo "${2%:}" +} + +iface_for_mac() { + local interface="" + local mac + mac="$(echo "$@" | sed 'y/ABCDEF/abcdef/')" + for interface in /sys/class/net/*; do + if [ "$(cat "$interface"/address)" = "$mac" ]; then + echo "${interface##*/}" + fi + done +} + +# get the iface name for the given identifier - either a MAC, IP, or iface name +iface_name() { + case "$1" in + ??:??:??:??:??:?? | ??-??-??-??-??-??) iface_for_mac "$1" ;; + *:*:* | *.*.*.*) iface_for_ip "$1" ;; + *) echo "$1" ;; + esac +} + +# list the configured interfaces +configured_ifaces() { + local IFACES="" iface_id="" rv=1 + [ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces + if { pidof udevd || pidof systemd-udevd; } > /dev/null; then + for iface_id in $IFACES; do + printf "%s\n" "$(iface_name "$iface_id")" + rv=0 + done + else + warn "configured_ifaces called before udev is running" + echo "$IFACES" + [ -n "$IFACES" ] && rv=0 + fi + return $rv +} + +all_ifaces_up() { + local iface="" IFACES="" + [ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces + for iface in $IFACES; do + [ -e /tmp/net."$iface".up ] || return 1 + done +} + +all_ifaces_setup() { + local iface="" IFACES="" + [ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces + for iface in $IFACES; do + [ -e /tmp/net."$iface".did-setup ] || return 1 + done +} + +get_netroot_ip() { + local prefix="" server="" rest="" + splitsep ":" "$1" prefix server rest + case $server in + [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*) + echo "$server" + return 0 + ;; + esac + return 1 +} + +ip_is_local() { + li=$(ip route get "$@" 2> /dev/null) + if [ -n "$li" ]; then + strstr "$li" " via " || return 0 + fi + return 1 +} + +ifdown() { + local netif="$1" + # ip down/flush ensures that routing info goes away as well + ip link set "$netif" down + ip addr flush dev "$netif" + echo "#empty" > /etc/resolv.conf + rm -f -- /tmp/net."$netif".did-setup + [ -z "$DO_VLAN" ] \ + && [ -e /sys/class/net/"$netif"/address ] \ + && rm -f -- "/tmp/net.$(cat /sys/class/net/"$netif"/address).did-setup" + # TODO: send "offline" uevent? +} + +setup_net() { + local netif="$1" f="" gw_ip="" netroot_ip="" iface="" IFACES="" + local _p + [ -e /tmp/net."$netif".did-setup ] && return + [ -z "$DO_VLAN" ] \ + && [ -e /sys/class/net/"$netif"/address ] \ + && [ -e "/tmp/net.$(cat /sys/class/net/"$netif"/address).did-setup" ] && return + [ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces + [ -z "$IFACES" ] && IFACES="$netif" + # run the scripts written by ifup + # shellcheck disable=SC1090 + [ -e /tmp/net."$netif".hostname ] && . /tmp/net."$netif".hostname + # shellcheck disable=SC1090 + [ -e /tmp/net."$netif".override ] && . /tmp/net."$netif".override + # shellcheck disable=SC1090 + [ -e /tmp/dhclient."$netif".dhcpopts ] && . /tmp/dhclient."$netif".dhcpopts + # set up resolv.conf + [ -e /tmp/net."$netif".resolv.conf ] \ + && awk '!array[$0]++' /tmp/net."$netif".resolv.conf > /etc/resolv.conf + # shellcheck disable=SC1090 + [ -e /tmp/net."$netif".gw ] && . /tmp/net."$netif".gw + + # add static route + for _p in $(getargs rd.route); do + route_to_var "$_p" || continue + [ -n "$route_dev" ] && [ "$route_dev" != "$netif" ] && continue + ip route add "$route_mask" ${route_gw:+via $route_gw} ${route_dev:+dev $route_dev} + if strstr "$route_mask" ":"; then + printf -- "%s\n" "$route_mask ${route_gw:+via $route_gw} ${route_dev:+dev $route_dev}" \ + > /tmp/net.route6."$netif" + else + printf -- "%s\n" "$route_mask ${route_gw:+via $route_gw} ${route_dev:+dev $route_dev}" \ + > /tmp/net.route."$netif" + fi + done + + # If a static route was necessary to reach the gateway, the + # first gateway setup call will have failed with + # RTNETLINK answers: Network is unreachable + # Replace the default route again after static routes to cover + # this scenario. + # shellcheck disable=SC1090 + [ -e /tmp/net."$netif".gw ] && . /tmp/net."$netif".gw + + # Handle STP Timeout: arping the default gateway. + # (or the root server, if a) it's local or b) there's no gateway.) + # Note: This assumes that if no router is present the + # root server is on the same subnet. + + # Get DHCP-provided router IP, or the cmdline-provided "gw=" argument + [ -n "$new_routers" ] && gw_ip=${new_routers%%,*} + [ -n "$gw" ] && gw_ip=$gw + + # Get the "netroot" IP (if there's an IP address in there) + netroot_ip=$(get_netroot_ip "$netroot") + + # try netroot if it's local (or there's no gateway) + if ip_is_local "$netroot_ip" || [ -z "$gw_ip" ]; then + dest="$netroot_ip" + else + dest="$gw_ip" + fi + + unset layer2 + if [ -f /sys/class/net/"$netif"/device/layer2 ]; then + read -r layer2 < /sys/class/net/"$netif"/device/layer2 + fi + + if [ "$layer2" != "0" ] && [ -n "$dest" ] && ! strstr "$dest" ":"; then + if command -v arping2 > /dev/null; then + arping2 -q -C 1 -c 60 -I "$netif" "$dest" || info "Resolving $dest via ARP on $netif failed" + else + arping -q -f -w 60 -I "$netif" "$dest" || info "Resolving $dest via ARP on $netif failed" + fi + fi + unset layer2 + + : > /tmp/net."$netif".did-setup + [ -z "$DO_VLAN" ] \ + && [ -e /sys/class/net/"$netif"/address ] \ + && : > "/tmp/net.$(cat /sys/class/net/"$netif"/address).did-setup" +} + +save_netinfo() { + local netif="$1" IFACES="" f="" i="" + [ -e /tmp/net.ifaces ] && read -r IFACES < /tmp/net.ifaces + # Add $netif to the front of IFACES (if it's not there already). + set -- "$netif" + for i in $IFACES; do [ "$i" != "$netif" ] && set -- "$@" "$i"; done + IFACES="$*" + for i in $IFACES; do + for f in "/tmp/dhclient.$i."*; do + [ -f "$f" ] && cp -f "$f" /tmp/net."${f#/tmp/dhclient.}" + done + done + echo "$IFACES" > /tmp/.net.ifaces.new + mv /tmp/.net.ifaces.new /tmp/net.ifaces +} + +set_ifname() { + local name="$1" mac="$2" num=-1 n="" + # if it's already set, return the existing name + for n in $(getargs ifname=); do + strstr "$n" "$mac" && echo "${n%%:*}" && return + done + [ ! -f "/tmp/set_ifname_$name" ] || read -r num < "/tmp/set_ifname_$name" + # otherwise, pick a new name and use that + while :; do + num=$((num + 1)) + [ -e /sys/class/net/"$name"$num ] && continue + for n in $(getargs ifname=); do + [ "$name$num" = "${n%%:*}" ] && continue 2 + done + break + done + echo "ifname=$name$num:$mac" >> /etc/cmdline.d/45-ifname.conf + echo "$num" > "/tmp/set_ifname_$name" + echo "$name$num" +} + +# pxelinux provides macaddr '-' separated, but we need ':' +fix_bootif() { + local macaddr="${1}" + local IFS='-' + # shellcheck disable=SC2086 + macaddr=$(printf '%s:' ${macaddr}) + macaddr=${macaddr%:} + # strip hardware type field from pxelinux + [ -n "${macaddr%??:??:??:??:??:??}" ] && macaddr=${macaddr#??:} + # return macaddr with lowercase alpha characters expected by udev + echo "$macaddr" | sed 'y/ABCDEF/abcdef/' +} + +ibft_to_cmdline() { + local iface="" + modprobe -q iscsi_ibft + ( + for iface in /sys/firmware/ibft/ethernet*; do + local mac="" dev="" + local dhcp="" ip="" gw="" mask="" hostname="" + local dns1 dns2 + + [ -e "${iface}"/mac ] || continue + read -r mac < "${iface}"/mac + [ -z "$mac" ] && continue + dev=$(set_ifname ibft "$mac") + + [ -e /tmp/net."${dev}".has_ibft_config ] && continue + + [ -e "${iface}"/flags ] && read -r flags < "${iface}"/flags + # Skip invalid interfaces + awk -- 'BEGIN { exit (!and('"$flags"',1)) }' || continue + # Skip interfaces not used for booting unless using multipath + if ! getargbool 0 rd.iscsi.mp; then + awk -- 'BEGIN { exit (!and('"$flags"',2)) }' || continue + fi + [ -e "${iface}"/dhcp ] && read -r dhcp < "${iface}"/dhcp + [ -e "${iface}"/origin ] && read -r origin < "${iface}"/origin + [ -e "${iface}"/ip-addr ] && read -r ip < "${iface}"/ip-addr + + if [ -n "$ip" ]; then + case "$ip" in + *.*.*.*) + family=ipv4 + ;; + *:*) + family=ipv6 + ;; + esac + fi + if [ -n "$dhcp" ] || [ "$origin" -eq 3 ]; then + if [ "$family" = "ipv6" ]; then + echo "ip=$dev:dhcp6" + else + echo "ip=$dev:dhcp" + fi + elif [ -e "${iface}"/ip-addr ]; then + # skip not assigned ip addresses + [ "$ip" = "0.0.0.0" ] && continue + [ -e "${iface}"/gateway ] && read -r gw < "${iface}"/gateway + [ "$gw" = "0.0.0.0" ] && unset gw + [ -e "${iface}"/subnet-mask ] && read -r mask < "${iface}"/subnet-mask + [ -e "${iface}"/prefix-len ] && read -r prefix < "${iface}"/prefix-len + [ -e "${iface}"/primary-dns ] && read -r dns1 < "${iface}"/primary-dns + [ "$dns1" = "0.0.0.0" ] && unset dns1 + [ -e "${iface}"/secondary-dns ] && read -r dns2 < "${iface}"/secondary-dns + [ "$dns2" = "0.0.0.0" ] && unset dns + [ -e "${iface}"/hostname ] && read -r hostname < "${iface}"/hostname + if [ "$family" = "ipv6" ]; then + if [ -n "$ip" ]; then + [ -n "$prefix" ] || prefix=128 + ip="[${ip}]" + mask=$prefix + fi + if [ -n "$gw" ]; then + gw="[${gw}]" + fi + fi + if [ -n "$ip" ] && [ -n "$mask" -o -n "$prefix" ]; then + echo "ip=$ip::$gw:$mask:$hostname:$dev:none${dns1:+:$dns1}${dns2:+:$dns2}" + else + warn "${iface} does not contain a valid iBFT configuration" + warn "ip-addr=$ip" + warn "gateway=$gw" + warn "subnet-mask=$mask" + warn "hostname=$hostname" + fi + else + info "${iface} does not contain a valid iBFT configuration" + # shellcheck disable=SC2012 + ls -l "${iface}" | vinfo + fi + + if [ -e "${iface}"/vlan ]; then + read -r vlan < "${iface}"/vlan + if [ "$vlan" -ne "0" ]; then + case "$vlan" in + [0-9]*) + echo "vlan=$dev.$vlan:$dev" + echo "$mac" > /tmp/net."${dev}"."${vlan}".has_ibft_config + ;; + *) + echo "vlan=$vlan:$dev" + echo "$mac" > /tmp/net."${vlan}".has_ibft_config + ;; + esac + else + echo "$mac" > /tmp/net."${dev}".has_ibft_config + fi + else + echo "$mac" > /tmp/net."${dev}".has_ibft_config + fi + + done + ) >> /etc/cmdline.d/40-ibft.conf +} + +parse_iscsi_root() { + local v + v=${1#iscsi:} + + # extract authentication info + case "$v" in + *@*:*:*:*:*) + authinfo=${v%%@*} + v=${v#*@} + # allow empty authinfo to allow having an @ in iscsi_target_name like this: + # netroot=iscsi:@192.168.1.100::3260::iqn.2009-01.com.example:testdi@sk + if [ -n "$authinfo" ]; then + OLDIFS="$IFS" + IFS=: + # shellcheck disable=SC2086 + set $authinfo + IFS="$OLDIFS" + if [ $# -gt 4 ]; then + warn "Wrong authentication info in iscsi: parameter!" + return 1 + fi + iscsi_username=$1 + iscsi_password=$2 + if [ $# -gt 2 ]; then + iscsi_in_username=$3 + iscsi_in_password=$4 + fi + fi + ;; + esac + + # extract target ip + case "$v" in + [[]*[]]:*) + iscsi_target_ip=${v#[[]} + iscsi_target_ip=${iscsi_target_ip%%[]]*} + v=${v#[[]"$iscsi_target_ip"[]]:} + ;; + *) + iscsi_target_ip=${v%%[:]*} + v=${v#"$iscsi_target_ip":} + ;; + esac + + unset iscsi_target_name + # extract target name + case "$v" in + *:iqn.*) + iscsi_target_name=iqn.${v##*:iqn.} + v=${v%:iqn.*}: + ;; + *:eui.*) + iscsi_target_name=eui.${v##*:eui.} + v=${v%:eui.*}: + ;; + *:naa.*) + iscsi_target_name=naa.${v##*:naa.} + v=${v%:naa.*}: + ;; + esac + + # parse the rest + OLDIFS="$IFS" + IFS=: + # shellcheck disable=SC2086 + set $v + IFS="$OLDIFS" + + iscsi_protocol=$1 + shift # ignored + iscsi_target_port=$1 + shift + + if [ -n "$iscsi_target_name" ]; then + if [ $# -eq 3 ]; then + iscsi_iface_name=$1 + shift + fi + if [ $# -eq 2 ]; then + iscsi_netdev_name=$1 + shift + fi + iscsi_lun=$1 + shift + if [ $# -ne 0 ]; then + warn "Invalid parameter in iscsi: parameter!" + return 1 + fi + return 0 + fi + + if [ $# -gt 3 ] && [ -n "$1$2" ]; then + if [ -z "$3" ] || [ "$3" -ge 0 ] 2> /dev/null; then + iscsi_iface_name=$1 + shift + iscsi_netdev_name=$1 + shift + fi + fi + + iscsi_lun=$1 + shift + + iscsi_target_name=$(printf "%s:" "$@") + iscsi_target_name=${iscsi_target_name%:} +} + +ip_to_var() { + local v="${1}": + local i + set -- + while [ -n "$v" ]; do + if [ "${v#\[*:*:*\]:}" != "$v" ]; then + # handle IPv6 address + i="${v%%\]:*}" + i="${i##\[}" + set -- "$@" "$i" + v=${v#\["$i"\]:} + else + set -- "$@" "${v%%:*}" + v=${v#*:} + fi + done + + unset ip srv gw mask hostname dev autoconf macaddr mtu dns1 dns2 + + if [ $# -eq 0 ]; then + autoconf="error" + return 0 + fi + + if [ $# -eq 1 ]; then + # format: ip={dhcp|on|any|dhcp6|auto6|either6|single-dhcp} + # or + # ip=<ipv4-address> means anaconda-style static config argument cluster + autoconf="$1" + + if strglob "$autoconf" "*.*.*.*"; then + # ip=<ipv4-address> means anaconda-style static config argument cluster: + # ip=<ip> gateway=<gw> netmask=<nm> hostname=<host> mtu=<mtu> + # ksdevice={link|bootif|ibft|<MAC>|<ifname>} + ip="$autoconf" + gw=$(getarg gateway=) + mask=$(getarg netmask=) + hostname=$(getarg hostname=) + dev=$(getarg ksdevice=) + autoconf="none" + mtu=$(getarg mtu=) + + # handle special values for ksdevice + case "$dev" in + bootif | BOOTIF) dev=$(fix_bootif "$(getarg BOOTIF=)") ;; + link) dev="" ;; # FIXME: do something useful with this + ibft) dev="" ;; # ignore - ibft is handled elsewhere + esac + fi + return 0 + fi + + if [ "$2" = "dhcp" -o "$2" = "on" -o "$2" = "any" -o "$2" = "dhcp6" -o "$2" = "auto6" -o "$2" = "either6" ]; then + # format: ip=<interface>:{dhcp|on|any|dhcp6|auto6}[:[<mtu>][:<macaddr>]] + [ -n "$1" ] && dev="$1" + [ -n "$2" ] && autoconf="$2" + [ -n "$3" ] && mtu=$3 + if [ -z "$5" ]; then + macaddr="$4" + else + macaddr="${4}:${5}:${6}:${7}:${8}:${9}" + fi + return 0 + fi + + # format: ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}:[:[<mtu>][:<macaddr>]] + + [ -n "$1" ] && ip=$1 + [ -n "$2" ] && srv=$2 + [ -n "$3" ] && gw=$3 + [ -n "$4" ] && mask=$4 + [ -n "$5" ] && hostname=$5 + [ -n "$6" ] && dev=$6 + [ -n "$7" ] && autoconf=$7 + case "$8" in + [0-9a-fA-F]*:* | [0-9]*.[0-9]*.[0-9]*.[0-9]*) + dns1="$8" + [ -n "$9" ] && dns2="$9" + ;; + [0-9]*) + mtu="$8" + if [ -n "${9}" -a -z "${10}" ]; then + macaddr="${9}" + elif [ -n "${9}" -a -n "${10}" -a -n "${11}" -a -n "${12}" -a -n "${13}" -a -n "${14}" ]; then + macaddr="${9}:${10}:${11}:${12}:${13}:${14}" + fi + ;; + *) + if [ -n "${9}" -a -z "${10}" ]; then + macaddr="${9}" + elif [ -n "${9}" -a -n "${10}" -a -n "${11}" -a -n "${12}" -a -n "${13}" -a -n "${14}" ]; then + macaddr="${9}:${10}:${11}:${12}:${13}:${14}" + fi + ;; + esac + return 0 +} + +route_to_var() { + local v="${1}": + local i + set -- + while [ -n "$v" ]; do + if [ "${v#\[*:*:*\]:}" != "$v" ]; then + # handle IPv6 address + i="${v%%\]:*}" + i="${i##\[}" + set -- "$@" "$i" + v=${v#\["$i"\]:} + else + set -- "$@" "${v%%:*}" + v=${v#*:} + fi + done + + unset route_mask route_gw route_dev + case $# in + 2) + [ -n "$1" ] && route_mask="$1" + [ -n "$2" ] && route_gw="$2" + return 0 + ;; + 3) + [ -n "$1" ] && route_mask="$1" + [ -n "$2" ] && route_gw="$2" + [ -n "$3" ] && route_dev="$3" + return 0 + ;; + *) return 1 ;; + esac +} + +parse_ifname_opts() { + local IFS=: + # shellcheck disable=SC2086 + set -- $1 + + case $# in + 7) + ifname_if=$1 + # udev requires MAC addresses to be lower case + ifname_mac=$(echo "$2:$3:$4:$5:$6:$7" | sed 'y/ABCDEF/abcdef/') + ;; + 21) + ifname_if=$1 + # udev requires MAC addresses to be lower case + ifname_mac=$(echo "$2:$3:$4:$5:$6:$7:$8:$9:${10}:${11}:${12}:${13}:${14}:${15}:${16}:${17}:${18}:${19}:${20}:${21}" | sed 'y/ABCDEF/abcdef/') + ;; + *) + die "Invalid arguments for ifname=" + ;; + esac + + case $ifname_if in + eth[0-9] | eth[0-9][0-9] | eth[0-9][0-9][0-9] | eth[0-9][0-9][0-9][0-9]) + warn "ifname=$ifname_if uses the kernel name space for interfaces" + warn "This can fail for multiple network interfaces and is discouraged!" + warn 'Please use a custom name like "netboot" or "bluesocket"' + warn "or use biosdevname and no ifname= at all." + ;; + esac + +} + +# some network driver need long time to initialize, wait before it's ready. +wait_for_if_link() { + local cnt=0 + local li + local timeout + timeout=$(getargs rd.net.timeout.iflink=) + timeout=${timeout:-60} + timeout=$((timeout * 10)) + + while [ $cnt -lt $timeout ]; do + li=$(ip link show dev "$@" 2> /dev/null) + [ -n "$li" ] && return 0 + sleep 0.1 + cnt=$((cnt + 1)) + done + return 1 +} + +wait_for_if_up() { + local cnt=0 + local li + local timeout + timeout=$(getargs rd.net.timeout.ifup=) + timeout=${timeout:-20} + timeout=$((timeout * 10)) + + while [ $cnt -lt $timeout ]; do + li=$(ip link show up dev "$@") + if [ -n "$li" ]; then + case "$li" in + *\<UP*) + return 0 + ;; + *\<*,UP\>*) + return 0 + ;; + *\<*,UP,*\>*) + return 0 + ;; + esac + fi + if strstr "$li" "LOWER_UP" \ + && strstr "$li" "state UNKNOWN" \ + && ! strstr "$li" "DORMANT"; then + return 0 + fi + sleep 0.1 + cnt=$((cnt + 1)) + done + return 1 +} + +wait_for_route_ok() { + local cnt=0 + local timeout + timeout=$(getargs rd.net.timeout.route=) + timeout=${timeout:-20} + timeout=$((timeout * 10)) + + while [ $cnt -lt $timeout ]; do + li=$(ip route show) + [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0 + sleep 0.1 + cnt=$((cnt + 1)) + done + return 1 +} + +wait_for_ipv6_dad_link() { + local cnt=0 + local timeout + timeout=$(getargs rd.net.timeout.ipv6dad=) + timeout=${timeout:-50} + timeout=$((timeout * 10)) + + while [ $cnt -lt $timeout ]; do + [ -n "$(ip -6 addr show dev "$@" scope link)" ] \ + && [ -z "$(ip -6 addr show dev "$@" scope link tentative)" ] \ + && return 0 + [ -n "$(ip -6 addr show dev "$@" scope link dadfailed)" ] \ + && return 1 + sleep 0.1 + cnt=$((cnt + 1)) + done + return 1 +} + +wait_for_ipv6_dad() { + local cnt=0 + local timeout + timeout=$(getargs rd.net.timeout.ipv6dad=) + timeout=${timeout:-50} + timeout=$((timeout * 10)) + + while [ $cnt -lt $timeout ]; do + [ -n "$(ip -6 addr show dev "$@")" ] \ + && [ -z "$(ip -6 addr show dev "$@" tentative)" ] \ + && return 0 + [ -n "$(ip -6 addr show dev "$@" dadfailed)" ] \ + && return 1 + sleep 0.1 + cnt=$((cnt + 1)) + done + return 1 +} + +wait_for_ipv6_auto() { + local cnt=0 + local timeout + timeout=$(getargs rd.net.timeout.ipv6auto=) + timeout=${timeout:-40} + timeout=$((timeout * 10)) + + while [ $cnt -lt $timeout ]; do + [ -z "$(ip -6 addr show dev "$@" tentative)" ] \ + && { ip -6 route list proto ra dev "$@" | grep -q ^default; } \ + && return 0 + sleep 0.1 + cnt=$((cnt + 1)) + done + return 1 +} + +linkup() { + wait_for_if_link "$@" 2> /dev/null && ip link set "$@" up 2> /dev/null && wait_for_if_up "$@" 2> /dev/null +} + +type hostname > /dev/null 2>&1 \ + || hostname() { + cat /proc/sys/kernel/hostname + } + +iface_has_carrier() { + local cnt=0 + local iface="$1" flags="" + local timeout + local iface_sys_path + [ -n "$iface" ] || return 2 + iface_sys_path="/sys/class/net/$iface" + [ -d "$iface_sys_path" ] || return 2 + timeout=$(getargs rd.net.timeout.carrier=) + timeout=${timeout:-10} + timeout=$((timeout * 10)) + + linkup "$1" + + li=$(ip link show up dev "$iface") + strstr "$li" "NO-CARRIER" && _no_carrier_flag=1 + + while [ $cnt -lt $timeout ]; do + if [ -n "$_no_carrier_flag" ]; then + li=$(ip link show up dev "$iface") + # NO-CARRIER flag was cleared + strstr "$li" "NO-CARRIER" || return 0 + elif ! [ -e "$iface_sys_path/carrier" ]; then + # sysfs not available and "NO-CARRIER" not displayed + return 0 + fi + # double check the syscfs carrier flag + [ -e "$iface_sys_path/carrier" ] && [ "$(cat "$iface_sys_path"/carrier)" = 1 ] && return 0 + sleep 0.1 + cnt=$((cnt + 1)) + done + return 1 +} + +iface_has_link() { + iface_has_carrier "$@" +} + +iface_is_enslaved() { + local _li + _li=$(ip link show dev "$@") + strstr "$_li" " master " || return 1 + return 0 +} + +find_iface_with_link() { + local iface_path="" iface="" + for iface_path in /sys/class/net/*; do + iface=${iface_path##*/} + str_starts "$iface" "lo" && continue + if iface_has_link "$iface"; then + echo "$iface" + return 0 + fi + done + return 1 +} + +is_persistent_ethernet_name() { + local _netif="$1" + local _name_assign_type="0" + + [ -f "/sys/class/net/$_netif/name_assign_type" ] \ + && read -r _name_assign_type < "/sys/class/net/$_netif/name_assign_type" 2> /dev/null + + # NET_NAME_ENUM 1 + [ "$_name_assign_type" = "1" ] && return 1 + + # NET_NAME_PREDICTABLE 2 + [ "$_name_assign_type" = "2" ] && return 0 + + case "$_netif" in + # udev persistent interface names + eno[0-9] | eno[0-9][0-9] | eno[0-9][0-9][0-9]*) ;; + + ens[0-9] | ens[0-9][0-9] | ens[0-9][0-9][0-9]*) ;; + + enp[0-9]s[0-9]* | enp[0-9][0-9]s[0-9]* | enp[0-9][0-9][0-9]*s[0-9]*) ;; + + enP*p[0-9]s[0-9]* | enP*p[0-9][0-9]s[0-9]* | enP*p[0-9][0-9][0-9]*s[0-9]*) ;; + + # biosdevname + em[0-9] | em[0-9][0-9] | em[0-9][0-9][0-9]*) ;; + + p[0-9]p[0-9]* | p[0-9][0-9]p[0-9]* | p[0-9][0-9][0-9]*p[0-9]*) ;; + + *) + return 1 + ;; + esac + return 0 +} + +is_kernel_ethernet_name() { + local _netif="$1" + local _name_assign_type="1" + + if [ -e "/sys/class/net/$_netif/name_assign_type" ]; then + read -r _name_assign_type < "/sys/class/net/$_netif/name_assign_type" + + case "$_name_assign_type" in + 2 | 3 | 4) + # NET_NAME_PREDICTABLE 2 + # NET_NAME_USER 3 + # NET_NAME_RENAMED 4 + return 1 + ;; + 1 | *) + # NET_NAME_ENUM 1 + return 0 + ;; + esac + fi + + # fallback to error prone manual name check + case "$_netif" in + eth[0-9] | eth[0-9][0-9] | eth[0-9][0-9][0-9]*) + return 0 + ;; + *) + return 1 + ;; + esac + +} + +iface_get_subchannels() { + local _netif + local _subchannels + + _netif="$1" + + _subchannels=$({ + for i in /sys/class/net/"$_netif"/device/cdev[0-9]*; do + [ -e "$i" ] || continue + channel=$(readlink -f "$i") + printf -- "%s" "${channel##*/}," + done + }) + [ -n "$_subchannels" ] || return 1 + + printf -- "%s" "${_subchannels%,}" +} diff --git a/modules.d/40network/netroot.sh b/modules.d/40network/netroot.sh new file mode 100755 index 0000000..8f97774 --- /dev/null +++ b/modules.d/40network/netroot.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin +command -v getarg > /dev/null || . /lib/dracut-lib.sh +command -v setup_net > /dev/null || . /lib/net-lib.sh + +# Huh? Empty $1? +[ -z "$1" ] && exit 1 + +# [ ! -z $2 ] means this is for manually bringing up network +# instead of real netroot; If It's called without $2, then there's +# no sense in doing something if no (net)root info is available +# or root is already there +[ -d "$NEWROOT"/proc ] && exit 0 + +if [ -z "$netroot" ]; then + netroot=$(getarg netroot=) +fi + +[ -z "$netroot" ] && exit 1 + +# Set or override primary interface +netif=$1 +[ -e "/tmp/net.bootdev" ] && read -r netif < /tmp/net.bootdev + +case "$netif" in + ??:??:??:??:??:??) # MAC address + for i in /sys/class/net/*/address; do + read -r mac < "$i" + if [ "$mac" = "$netif" ]; then + i=${i%/address} + netif=${i##*/} + break + fi + done ;; +esac + +# Figure out the handler for root=dhcp by recalling all netroot cmdline +# handlers when this is not called from manually network bringing up. +if [ -z "$2" ]; then + if getarg "root=dhcp" || getarg "netroot=dhcp" || getarg "root=dhcp6" || getarg "netroot=dhcp6"; then + # Load dhcp options + # shellcheck disable=SC1090 + [ -e /tmp/dhclient."$netif".dhcpopts ] && . /tmp/dhclient."$netif".dhcpopts + + # If we have a specific bootdev with no dhcpoptions or empty root-path, + # we die. Otherwise we just warn + if [ -z "$new_root_path" ]; then + [ -n "$BOOTDEV" ] && die "No dhcp root-path received for '$BOOTDEV'" + warn "No dhcp root-path received for '$netif' trying other interfaces if available" + exit 1 + fi + + rm -f -- "$hookdir"/initqueue/finished/dhcp.sh + + # Set netroot to new_root_path, so cmdline parsers don't call + netroot=$new_root_path + + # FIXME! + unset rootok + for f in "$hookdir"/cmdline/90*.sh; do + # shellcheck disable=SC1090 + [ -f "$f" ] && . "$f" + done + else + rootok="1" + fi + + # Check: do we really know how to handle (net)root? + if [ -z "$root" ]; then + root=$(getarg root=) + fi + [ -z "$root" ] && die "No or empty root= argument" + [ -z "$rootok" ] && die "Don't know how to handle 'root=$root'" + + handler=${netroot%%:*} + handler=${handler%%4} + handler=$(command -v "${handler}"root) + if [ -z "$netroot" ] || [ ! -e "$handler" ]; then + die "No handler for netroot type '$netroot'" + fi +fi + +# Source netroot hooks before we start the handler +source_hook netroot "$netif" + +# Run the handler; don't store the root, it may change from device to device +# XXX other variables to export? +[ -n "$handler" ] && "$handler" "$netif" "$netroot" "$NEWROOT" +save_netinfo "$netif" + +exit 0 diff --git a/modules.d/45ifcfg/module-setup.sh b/modules.d/45ifcfg/module-setup.sh new file mode 100755 index 0000000..b0d4418 --- /dev/null +++ b/modules.d/45ifcfg/module-setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# called by dracut +check() { + [[ -d $dracutsysrootdir/etc/sysconfig/network-scripts ]] && return 0 + return 255 +} + +# called by dracut +depends() { + echo "network" + return 0 +} + +# called by dracut +install() { + inst_binary awk + inst_hook pre-pivot 85 "$moddir/write-ifcfg.sh" +} diff --git a/modules.d/45ifcfg/write-ifcfg.sh b/modules.d/45ifcfg/write-ifcfg.sh new file mode 100755 index 0000000..5550cce --- /dev/null +++ b/modules.d/45ifcfg/write-ifcfg.sh @@ -0,0 +1,311 @@ +#!/bin/sh + +# NFS root might have reached here before /tmp/net.ifaces was written +type is_persistent_ethernet_name > /dev/null 2>&1 || . /lib/net-lib.sh + +udevadm settle --timeout=30 + +# shellcheck disable=SC2174 +mkdir -m 0755 -p /tmp/ifcfg/ +# shellcheck disable=SC2174 +mkdir -m 0755 -p /tmp/ifcfg-leases/ + +get_config_line_by_subchannel() { + local CHANNELS + local line + + CHANNELS="$1" + while read -r line || [ -n "$line" ]; do + if strstr "$line" "$CHANNELS"; then + echo "$line" + return 0 + fi + done < /etc/ccw.conf + return 1 +} + +print_s390() { + local _netif + local SUBCHANNELS + local OPTIONS + local NETTYPE + local CONFIG_LINE + local i + local channel + local OLD_IFS + + _netif="$1" + # if we find ccw channel, then use those, instead of + # of the MAC + SUBCHANNELS=$({ + for i in /sys/class/net/"$_netif"/device/cdev[0-9]*; do + [ -e "$i" ] || continue + channel=$(readlink -f "$i") + printf '%s' "${channel##*/}," + done + }) + [ -n "$SUBCHANNELS" ] || return 1 + + SUBCHANNELS=${SUBCHANNELS%,} + echo "SUBCHANNELS=\"${SUBCHANNELS}\"" + + CONFIG_LINE=$(get_config_line_by_subchannel "$SUBCHANNELS") + # shellcheck disable=SC2181 + [ $? -ne 0 -o -z "$CONFIG_LINE" ] && return 0 + + OLD_IFS=$IFS + IFS="," + # shellcheck disable=SC2086 + set -- $CONFIG_LINE + IFS=$OLD_IFS + NETTYPE=$1 + shift + SUBCHANNELS="$1" + OPTIONS="" + shift + while [ $# -gt 0 ]; do + case $1 in + *=*) OPTIONS="$OPTIONS $1" ;; + esac + shift + done + OPTIONS=${OPTIONS## } + echo "NETTYPE=\"${NETTYPE}\"" + echo "OPTIONS=\"${OPTIONS}\"" + return 0 +} + +hw_bind() { + local _netif="$1" + local _macaddr="$2" + + [ -n "$_macaddr" ] \ + && echo "MACADDR=\"$_macaddr\"" + + print_s390 "$_netif" \ + && return 0 + + [ -n "$_macaddr" ] && return 0 + + is_persistent_ethernet_name "$_netif" && return 0 + + [ -f "/sys/class/net/$_netif/addr_assign_type" ] \ + && [ "$(cat "/sys/class/net/$_netif/addr_assign_type")" != "0" ] \ + && return 1 + + [ -f "/sys/class/net/$_netif/address" ] \ + || return 1 + + echo "HWADDR=\"$(cat /sys/class/net/"$_netif"/address)\"" +} + +interface_bind() { + local _netif="$1" + local _macaddr="$2" + + if [ ! -e "/sys/class/net/$_netif" ]; then + warn "Cannot find network interface '$_netif'!" + return 1 + fi + + # see, if we can bind it to some hw params + if hw_bind "$_netif" "$_macaddr"; then + # only print out DEVICE, if it's user assigned + is_kernel_ethernet_name "$_netif" && return 0 + fi + + echo "DEVICE=\"$_netif\"" +} + +for netup in /tmp/net.*.did-setup; do + [ -f "$netup" ] || continue + + netif=${netup%%.did-setup} + netif=${netif##*/net.} + strglobin "$netif" ":*:*:*:*:" && continue + [ -e /tmp/ifcfg/ifcfg-"$netif" ] && continue + unset bridge + unset bond + unset bondslaves + unset bondname + unset bondoptions + unset bridgename + unset bridgeslaves + unset team + unset uuid + unset ip + unset gw + unset mtu + unset mask + unset macaddr + unset slave + unset ethname + unset vlan + unset vlanname + unset phydevice + + # shellcheck disable=SC1090 + [ -e /tmp/bond."${netif}".info ] && . /tmp/bond."${netif}".info + # shellcheck disable=SC1090 + [ -e /tmp/bridge."${netif}".info ] && . /tmp/bridge."${netif}".info + # shellcheck disable=SC1090 + [ -e /tmp/team."${netif}".info ] && . /tmp/team."${netif}".info + + read -r uuid < /proc/sys/kernel/random/uuid + if [ "$netif" = "$bridgename" ]; then + bridge=yes + elif [ "$netif" = "$teammaster" ]; then + team=yes + elif [ "$netif" = "$bondname" ]; then + # $netif can't be bridge and bond at the same time + bond=yes + fi + + for i in "/tmp/vlan.${netif}."*; do + [ ! -e "$i" ] && continue + # shellcheck disable=SC1090 + . "$i" + vlan=yes + break + done + + # skip team interfaces for now, the host config must be in sync + [ "$netif" = "$teammaster" ] && continue + + { + echo "# Generated by dracut initrd" + echo "NAME=\"$netif\"" + [ -z "$vlan" ] && interface_bind "$netif" "$macaddr" + echo "ONBOOT=yes" + echo "NETBOOT=yes" + echo "UUID=\"$uuid\"" + strstr "$(ip -6 addr show dev "$netif")" 'inet6' && echo "IPV6INIT=yes" + if [ -f /tmp/dhclient."$netif".lease ]; then + # shellcheck disable=SC1090 + [ -f /tmp/dhclient."$netif".dhcpopts ] && . /tmp/dhclient."$netif".dhcpopts + if [ -f /tmp/net."$netif".has_ibft_config ]; then + echo "BOOTPROTO=ibft" + else + echo "BOOTPROTO=dhcp" + fi + cp /tmp/dhclient."$netif".lease /tmp/ifcfg-leases/dhclient-"$uuid"-"$netif".lease + else + # If we've booted with static ip= lines, the override file is there + # shellcheck disable=SC1090 + [ -e /tmp/net."$netif".override ] && . /tmp/net."$netif".override + if strglobin "$ip" '*:*:*'; then + echo "IPV6INIT=yes" + echo "IPV6_AUTOCONF=no" + echo "IPV6ADDR=\"$ip/$mask\"" + else + if [ -f /tmp/net."$netif".has_ibft_config ]; then + echo "BOOTPROTO=ibft" + else + echo "BOOTPROTO=none" + echo "IPADDR=\"$ip\"" + if strstr "$mask" "."; then + echo "NETMASK=\"$mask\"" + else + echo "PREFIX=\"$mask\"" + fi + fi + fi + if strglobin "$gw" '*:*:*'; then + echo "IPV6_DEFAULTGW=\"$gw\"" + elif [ -n "$gw" ]; then + echo "GATEWAY=\"$gw\"" + fi + fi + [ -n "$mtu" ] && echo "MTU=\"$mtu\"" + } > /tmp/ifcfg/ifcfg-"$netif" + + # bridge needs different things written to ifcfg + if [ -z "$bridge" ] && [ -z "$bond" ] && [ -z "$vlan" ] && [ -z "$team" ]; then + # standard interface + echo "TYPE=Ethernet" >> /tmp/ifcfg/ifcfg-"$netif" + fi + + if [ -n "$vlan" ]; then + { + echo "TYPE=Vlan" + echo "DEVICE=\"$netif\"" + echo "VLAN=yes" + echo "PHYSDEV=\"$phydevice\"" + } >> /tmp/ifcfg/ifcfg-"$netif" + fi + + if [ -n "$bond" ]; then + # bond interface + { + # This variable is an indicator of a bond interface for initscripts + echo "BONDING_OPTS=\"$bondoptions\"" + echo "NAME=\"$netif\"" + echo "TYPE=Bond" + } >> /tmp/ifcfg/ifcfg-"$netif" + + for slave in $bondslaves; do + # write separate ifcfg file for the raw eth interface + ( + echo "# Generated by dracut initrd" + echo "NAME=\"$slave\"" + echo "TYPE=Ethernet" + echo "ONBOOT=yes" + echo "NETBOOT=yes" + echo "SLAVE=yes" + echo "MASTER=\"$netif\"" + echo "UUID=\"$(cat /proc/sys/kernel/random/uuid)\"" + unset macaddr + # shellcheck disable=SC1090 + [ -e /tmp/net."$slave".override ] && . /tmp/net."$slave".override + interface_bind "$slave" "$macaddr" + ) >> /tmp/ifcfg/ifcfg-"$slave" + done + fi + + if [ -n "$bridge" ]; then + # bridge + { + echo "TYPE=Bridge" + echo "NAME=\"$netif\"" + } >> /tmp/ifcfg/ifcfg-"$netif" + for slave in $bridgeslaves; do + # write separate ifcfg file for the raw eth interface + ( + echo "# Generated by dracut initrd" + echo "NAME=\"$slave\"" + echo "TYPE=Ethernet" + echo "ONBOOT=yes" + echo "NETBOOT=yes" + echo "BRIDGE=\"$bridgename\"" + echo "UUID=\"$(cat /proc/sys/kernel/random/uuid)\"" + unset macaddr + # shellcheck disable=SC1090 + [ -e /tmp/net."$slave".override ] && . /tmp/net."$slave".override + interface_bind "$slave" "$macaddr" + ) >> /tmp/ifcfg/ifcfg-"$slave" + done + fi + i=1 + for ns in $(getargs nameserver) $dns1 $dns2; do + echo "DNS${i}=\"${ns}\"" >> /tmp/ifcfg/ifcfg-"$netif" + i=$((i + 1)) + done + + [ -f /tmp/net.route6."$netif" ] && cp /tmp/net.route6."$netif" /tmp/ifcfg/route6-"$netif" + [ -f /tmp/net.route."$netif" ] && cp /tmp/net.route."$netif" /tmp/ifcfg/route-"$netif" +done + +# Pass network opts +mkdir -m 0755 -p /run/initramfs/state/etc/sysconfig/network-scripts +mkdir -m 0755 -p /run/initramfs/state/var/lib/dhclient +echo "files /etc/sysconfig/network-scripts" >> /run/initramfs/rwtab +echo "files /var/lib/dhclient" >> /run/initramfs/rwtab +{ + cp /tmp/net.* /run/initramfs/ + for i in /tmp/net.*.resolv.conf; do + [ -f "$i" ] && cat "$i" + done | awk '!($0 in a) { a[$0]; print }' > /run/initramfs/state/etc/resolv.conf + [ -s /run/initramfs/state/etc/resolv.conf ] || rm -f /run/initramfs/state/etc/resolv.conf + copytree /tmp/ifcfg /run/initramfs/state/etc/sysconfig/network-scripts + cp /tmp/ifcfg-leases/* /run/initramfs/state/var/lib/dhclient +} > /dev/null 2>&1 diff --git a/modules.d/45url-lib/module-setup.sh b/modules.d/45url-lib/module-setup.sh new file mode 100755 index 0000000..65da87e --- /dev/null +++ b/modules.d/45url-lib/module-setup.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# module-setup for url-lib + +# called by dracut +check() { + require_binaries curl || return 1 + return 255 +} + +# called by dracut +depends() { + echo network + return 0 +} + +# called by dracut +install() { + local _dir _crt _crts _found _lib _nssckbi _p11roots _p11root + inst_simple "$moddir/url-lib.sh" "/lib/url-lib.sh" + inst_multiple -o ctorrent + inst_multiple curl sed + if curl --version | grep -qi '\bNSS\b'; then + # also install libs for curl https + inst_libdir_file "libnsspem.so*" + inst_libdir_file "libnsssysinit.so*" + inst_libdir_file "libsoftokn3.so*" + inst_libdir_file "libsqlite3.so*" + fi + + for _dir in $libdirs; do + [[ -d $dracutsysrootdir$_dir ]] || continue + for _lib in "$dracutsysrootdir$_dir"/libcurl.so.* "$dracutsysrootdir$_dir"/libcrypto.so.*; do + [[ -e $_lib ]] || continue + if ! [[ $_nssckbi ]]; then + read -r -d '' _nssckbi < <(grep -F --binary-files=text -z libnssckbi "$_lib") + fi + read -r -d '' _crt < <(grep -E --binary-files=text -z "\.(pem|crt)" "$_lib" | sed 's/\x0//g') + [[ $_crt ]] || continue + [[ $_crt == /*/* ]] || continue + if [[ -e $_crt ]]; then + _crts="$_crts $_crt" + _found=1 + fi + done + done + if [[ $_found ]] && [[ -n $_crts ]]; then + for _crt in $_crts; do + if ! inst "${_crt#"$dracutsysrootdir"}"; then + dwarn "Couldn't install '$_crt' SSL CA cert bundle; HTTPS might not work." + continue + fi + done + fi + # If we found no cert bundle files referenced in libcurl but we + # *did* find a mention of libnssckbi (checked above), install it. + # If its truly NSS libnssckbi, it includes its own trust bundle, + # but if it's really p11-kit-trust.so, we need to find the dirs + # where it will look for a trust bundle and install them too. + if ! [[ $_found ]] && [[ $_nssckbi ]]; then + _found=1 + inst_libdir_file "libnssckbi.so*" || _found= + for _dir in $libdirs; do + [[ -e $dracutsysrootdir$_dir/libnssckbi.so ]] || continue + # this looks for directory-ish strings in the file + grep -z -o --binary-files=text '/[[:alpha:]][[:print:]]*' "${dracutsysrootdir}${_dir}"/libnssckbi.so \ + | while read -r -d '' _p11roots || [[ $_p11roots ]]; do + IFS=":" read -r -a _p11roots <<< "$_p11roots" + # the string can be a :-separated list of dirs + for _p11root in "${_p11roots[@]}"; do + # check if it's actually a directory (there are + # several false positives in the results) + [[ -d "$dracutsysrootdir$_p11root" ]] || continue + # check if it has some specific subdirs that all + # p11-kit trust dirs have + [[ -d "$dracutsysrootdir${_p11root}/anchors" ]] || continue + [[ -d "$dracutsysrootdir${_p11root}/blacklist" ]] || continue + # so now we know it's really a p11-kit trust dir; + # install everything in it + mkdir -p -- "${initdir}/${_p11root}" + if ! $DRACUT_CP -L -t "${initdir}/${_p11root}" "${dracutsysrootdir}${_p11root}"/*; then + dwarn "Couldn't install from p11-kit trust dir '${_p11root#"$dracutsysrootdir"}'; HTTPS might not work." + fi + done + done + done + fi + [[ $_found ]] || dwarn "Couldn't find SSL CA cert bundle or libnssckbi.so; HTTPS won't work." +} diff --git a/modules.d/45url-lib/url-lib.sh b/modules.d/45url-lib/url-lib.sh new file mode 100755 index 0000000..7c3ef1e --- /dev/null +++ b/modules.d/45url-lib/url-lib.sh @@ -0,0 +1,174 @@ +#!/bin/sh +# url-lib.sh - functions for handling URLs (file fetching etc.) +# +# Authors: +# Will Woods <wwoods@redhat.com> + +type mkuniqdir > /dev/null 2>&1 || . /lib/dracut-lib.sh + +# fetch_url URL [OUTFILE] +# fetch the given URL to a locally-visible location. +# if OUTFILE is given, the URL will be fetched to that filename, +# overwriting it if present. +# If the URL is something mountable (e.g. nfs://) and no OUTFILE is given, +# the server will be left mounted until pre-pivot. +# the return values are as follows: +# 0: success +# 253: unknown error (file missing) +# 254: unhandled URL scheme / protocol +# 255: bad arguments / unparsable URLs +# other: fetch command failure (whatever curl/mount/etc return) +fetch_url() { + local url="$1" outloc="$2" + local handler + handler="$(get_url_handler "$url")" + [ -n "$handler" ] || return 254 + [ -n "$url" ] || return 255 + "$handler" "$url" "$outloc" +} + +# get_url_handler URL +# returns the first HANDLERNAME corresponding to the URL's scheme +get_url_handler() { + local scheme="${1%%:*}" item="" + for item in $url_handler_map; do + [ "$scheme" = "${item%%:*}" ] && echo "${item#*:}" && return 0 + done + return 1 +} + +# add_url_handler HANDLERNAME SCHEME [SCHEME...] +# associate the named handler with the named scheme(s). +add_url_handler() { + local handler="$1" + shift + local schemes="$*" scheme="" + set -- + for scheme in $schemes; do + [ "$(get_url_handler "$scheme")" = "$handler" ] && continue + set -- "$@" "$scheme:$handler" + done + set -- "$@" "$url_handler_map" # add new items to *front* of list + url_handler_map="$*" +} + +### HTTP, HTTPS, FTP ################################################# + +export CURL_HOME="/run/initramfs/url-lib" +mkdir -p $CURL_HOME +curl_args="--globoff --location --retry 3 --retry-connrefused --fail --show-error" +getargbool 0 rd.noverifyssl && curl_args="$curl_args --insecure" + +proxy=$(getarg proxy=) +[ -n "$proxy" ] && curl_args="$curl_args --proxy $proxy" + +curl_fetch_url() { + local url="$1" outloc="$2" + echo "$url" > /proc/self/fd/0 + if [ -n "$outloc" ]; then + # shellcheck disable=SC2086 + curl $curl_args --output "$outloc" -- "$url" || return $? + else + local outdir + outdir="$(mkuniqdir /tmp curl_fetch_url)" + ( + cd "$outdir" || exit + # shellcheck disable=SC2086 + curl $curl_args --remote-name "$url" || return $? + ) + outloc="$outdir/$(ls -A "$outdir")" + fi + if ! [ -f "$outloc" ]; then + warn "Downloading '$url' failed!" + return 253 + fi + if [ -z "$2" ]; then echo "$outloc"; fi +} +add_url_handler curl_fetch_url http https ftp tftp + +set_http_header() { + echo "header = \"$1: $2\"" >> $CURL_HOME/.curlrc +} + +### TORRENT ########################################################## + +ctorrent_args="-E 0 -e 0" + +ctorrent_fetch_url() { + local url="$1" outloc="$2" + url=${url#*//} + torrent_outloc="$outloc.torrent" + echo "$url" > /proc/self/fd/0 + if [ -n "$outloc" ]; then + # shellcheck disable=SC2086 + curl $curl_args --output "$torrent_outloc" -- "$url" || return $? + else + local outdir + outdir="$(mkuniqdir /tmp torrent_fetch_url)" + ( + cd "$outdir" || exit + # shellcheck disable=SC2086 + curl $curl_args --remote-name "$url" || return $? + ) + torrent_outloc="$outdir/$(ls -A "$outdir")" + outloc=${torrent_outloc%.*} + fi + if ! [ -f "$torrent_outloc" ]; then + warn "Downloading '$url' failed!" + return 253 + fi + # shellcheck disable=SC2086 + ctorrent $ctorrent_args -s "$outloc" "$torrent_outloc" >&2 + if ! [ -f "$outloc" ]; then + warn "Torrent download of '$url' failed!" + return 253 + fi + if [ -z "$2" ]; then echo "$outloc"; fi +} + +command -v ctorrent > /dev/null \ + && add_url_handler ctorrent_fetch_url torrent + +### NFS ############################################################## + +[ -e /lib/nfs-lib.sh ] && . /lib/nfs-lib.sh + +nfs_already_mounted() { + local server="$1" path="$2" s="" p="" + while read -r src mnt rest || [ -n "$src" ]; do + splitsep ":" "$src" s p + p=${p%/} + if [ "$server" = "$s" ]; then + if [ "$path" = "$p" ]; then + echo "$mnt" + elif str_starts "$path" "$p"; then + echo "$mnt"/"${path#"$p"/}" + fi + fi + done < /proc/mounts +} + +nfs_fetch_url() { + local url="$1" outloc="$2" nfs="" server="" path="" options="" + nfs_to_var "$url" || return 255 + local filepath="${path%/*}" filename="${path##*/}" mntdir="" + + # skip mount if server:/filepath is already mounted + mntdir=$(nfs_already_mounted "$server" "$filepath") + if [ -z "$mntdir" ]; then + local mntdir + mntdir="$(mkuniqdir /run nfs_mnt)" + mount_nfs "$nfs:$server:$filepath${options:+:$options}" "$mntdir" + # lazy unmount during pre-pivot hook + inst_hook --hook pre-pivot --name 99url-lib-umount-nfs-"$(basename "$mntdir")" umount -l -- "$mntdir" + fi + + if [ -z "$outloc" ]; then + outloc="$mntdir/$filename" + else + cp -f -- "$mntdir/$filename" "$outloc" || return $? + fi + [ -f "$outloc" ] || return 253 + if [ -z "$2" ]; then echo "$outloc"; fi +} +command -v nfs_to_var > /dev/null && add_url_handler nfs_fetch_url nfs nfs4 diff --git a/modules.d/50drm/module-setup.sh b/modules.d/50drm/module-setup.sh new file mode 100755 index 0000000..1fb3867 --- /dev/null +++ b/modules.d/50drm/module-setup.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + # Include KMS capable drm drivers + + if [[ ${DRACUT_ARCH:-$(uname -m)} == arm* || ${DRACUT_ARCH:-$(uname -m)} == aarch64 ]]; then + # arm/aarch64 specific modules needed by drm + instmods \ + "=drivers/gpu/drm/i2c" \ + "=drivers/gpu/drm/panel" \ + "=drivers/gpu/drm/bridge" \ + "=drivers/video/backlight" + fi + + instmods amdkfd hyperv_fb "=drivers/pwm" + + # if the hardware is present, include module even if it is not currently loaded, + # as we could e.g. be in the installer; nokmsboot boot parameter will disable + # loading of the driver if needed + if [[ $hostonly ]]; then + local i modlink modname + + for i in /sys/bus/{pci/devices,platform/devices,virtio/devices,soc/devices/soc?,vmbus/devices}/*/modalias; do + [[ -e $i ]] || continue + [[ -n $(< "$i") ]] || continue + # shellcheck disable=SC2046 + if hostonly="" dracut_instmods --silent -s "drm_crtc_init|drm_dev_register|drm_encoder_init" -S "iw_handler_get_spy" $(< "$i"); then + if strstr "$(modinfo -F filename $(< "$i") 2> /dev/null)" radeon.ko; then + hostonly='' instmods amdkfd + fi + fi + done + # if there is a privacy screen then its driver must be loaded before the + # kms driver will bind, otherwise its probe() will return -EPROBE_DEFER + # note privacy screens always register, even with e.g. nokmsboot + for i in /sys/class/drm/privacy_screen-*/device/driver/module; do + [[ -L $i ]] || continue + modlink=$(readlink "$i") + modname=$(basename "$modlink") + instmods "$modname" + done + else + dracut_instmods -o -s "drm_crtc_init|drm_dev_register|drm_encoder_init" "=drivers/gpu/drm" "=drivers/staging" + # also include privacy screen providers (see above comment) + # atm all providers live under drivers/platform/x86 + dracut_instmods -o -s "drm_privacy_screen_register" "=drivers/platform/x86" + fi +} diff --git a/modules.d/50plymouth/module-setup.sh b/modules.d/50plymouth/module-setup.sh new file mode 100755 index 0000000..cc6629b --- /dev/null +++ b/modules.d/50plymouth/module-setup.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +pkglib_dir() { + local _dirs="/usr/lib/plymouth /usr/libexec/plymouth/" + if find_binary dpkg-architecture &> /dev/null; then + local _arch + _arch=$(dpkg-architecture -qDEB_HOST_MULTIARCH 2> /dev/null) + [ -n "$_arch" ] && _dirs+=" /usr/lib/$_arch/plymouth" + fi + for _dir in $_dirs; do + if [ -x "$dracutsysrootdir""$_dir"/plymouth-populate-initrd ]; then + echo "$_dir" + return + fi + done +} + +# called by dracut +check() { + [[ "$mount_needs" ]] && return 1 + [[ $(pkglib_dir) ]] || return 1 + + require_binaries plymouthd plymouth plymouth-set-default-theme +} + +# called by dracut +depends() { + echo drm +} + +# called by dracut +install() { + PKGLIBDIR=$(pkglib_dir) + if grep -q nash "$dracutsysrootdir""${PKGLIBDIR}"/plymouth-populate-initrd \ + || [ ! -x "$dracutsysrootdir""${PKGLIBDIR}"/plymouth-populate-initrd ]; then + # shellcheck disable=SC1090 + . "$moddir"/plymouth-populate-initrd.sh + else + PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$dracutfunctions" \ + "$dracutsysrootdir""${PKGLIBDIR}"/plymouth-populate-initrd -t "$initdir" + fi + + inst_hook emergency 50 "$moddir"/plymouth-emergency.sh + + inst_multiple readlink + + inst_multiple plymouthd plymouth plymouth-set-default-theme + + if ! dracut_module_included "systemd"; then + inst_hook pre-trigger 10 "$moddir"/plymouth-pretrigger.sh + inst_hook pre-pivot 90 "$moddir"/plymouth-newroot.sh + fi +} diff --git a/modules.d/50plymouth/plymouth-emergency.sh b/modules.d/50plymouth/plymouth-emergency.sh new file mode 100755 index 0000000..cf220b2 --- /dev/null +++ b/modules.d/50plymouth/plymouth-emergency.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +plymouth --hide-splash 2> /dev/null || : diff --git a/modules.d/50plymouth/plymouth-newroot.sh b/modules.d/50plymouth/plymouth-newroot.sh new file mode 100755 index 0000000..944f2dc --- /dev/null +++ b/modules.d/50plymouth/plymouth-newroot.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +if type plymouth > /dev/null 2>&1 && [ -z "$DRACUT_SYSTEMD" ]; then + plymouth --newroot="$NEWROOT" +fi diff --git a/modules.d/50plymouth/plymouth-populate-initrd.sh b/modules.d/50plymouth/plymouth-populate-initrd.sh new file mode 100755 index 0000000..7e3afdd --- /dev/null +++ b/modules.d/50plymouth/plymouth-populate-initrd.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +PLYMOUTH_LOGO_FILE="/usr/share/pixmaps/system-logo-white.png" +PLYMOUTH_THEME=$(plymouth-set-default-theme) + +inst_multiple plymouthd plymouth + +test -e "${PLYMOUTH_LOGO_FILE}" && inst_simple "${PLYMOUTH_LOGO_FILE}" + +# shellcheck disable=SC2174 +mkdir -m 0755 -p "${initdir}/usr/share/plymouth" + +inst_libdir_file "plymouth/text.so" "plymouth/details.so" + +if [[ $hostonly ]]; then + inst_multiple \ + "/usr/share/plymouth/themes/details/details.plymouth" \ + "/usr/share/plymouth/themes/text/text.plymouth" + + if [[ -d $dracutsysrootdir/usr/share/plymouth/themes/${PLYMOUTH_THEME} ]]; then + for x in "/usr/share/plymouth/themes/${PLYMOUTH_THEME}"/*; do + [[ -f "$dracutsysrootdir$x" ]] || break + inst "$x" + done + fi + + if [[ -L $dracutsysrootdir/usr/share/plymouth/themes/default.plymouth ]]; then + inst /usr/share/plymouth/themes/default.plymouth + # Install plugin for this theme + PLYMOUTH_PLUGIN=$(grep "^ModuleName=" "$dracutsysrootdir"/usr/share/plymouth/themes/default.plymouth | while read -r _ b _ || [ -n "$b" ]; do echo "$b"; done) + inst_libdir_file "plymouth/${PLYMOUTH_PLUGIN}.so" + fi +else + for x in "$dracutsysrootdir"/usr/share/plymouth/themes/{text,details}/*; do + [[ -f $x ]] || continue + THEME_DIR=$(dirname "${x#"$dracutsysrootdir"}") + # shellcheck disable=SC2174 + mkdir -m 0755 -p "${initdir}/$THEME_DIR" + inst_multiple "${x#"$dracutsysrootdir"}" + done + ( + cd "${initdir}"/usr/share/plymouth/themes || exit + ln -s text/text.plymouth default.plymouth 2>&1 + ) +fi diff --git a/modules.d/50plymouth/plymouth-pretrigger.sh b/modules.d/50plymouth/plymouth-pretrigger.sh new file mode 100755 index 0000000..3d11999 --- /dev/null +++ b/modules.d/50plymouth/plymouth-pretrigger.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +if type plymouthd > /dev/null 2>&1 && [ -z "$DRACUT_SYSTEMD" ]; then + if getargbool 1 plymouth.enable && getargbool 1 rd.plymouth -d -n rd_NO_PLYMOUTH; then + # first trigger graphics subsystem + udevadm trigger --action=add --attr-match=class=0x030000 > /dev/null 2>&1 + # first trigger graphics and tty subsystem + udevadm trigger --action=add \ + --subsystem-match=graphics \ + --subsystem-match=drm \ + --subsystem-match=tty \ + --subsystem-match=acpi \ + > /dev/null 2>&1 + + udevadm settle --timeout=180 2>&1 | vinfo + + info "Starting plymouth daemon" + mkdir -m 0755 /run/plymouth + read -r consoledev rest < /sys/class/tty/console/active + consoledev=${consoledev:-tty0} + [ -x /lib/udev/console_init -a -e "/dev/$consoledev" ] && /lib/udev/console_init "/dev/$consoledev" + plymouthd --attach-to-session --pid-file /run/plymouth/pid + plymouth --show-splash 2>&1 | vinfo + # reset tty after plymouth messed with it + [ -x /lib/udev/console_init -a -e "/dev/$consoledev" ] && /lib/udev/console_init "/dev/$consoledev" + fi +fi diff --git a/modules.d/62bluetooth/module-setup.sh b/modules.d/62bluetooth/module-setup.sh new file mode 100755 index 0000000..8bef927 --- /dev/null +++ b/modules.d/62bluetooth/module-setup.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + # If the binary(s) requirements are not fulfilled the module can't be installed + require_any_binary /usr/lib/bluetooth/bluetoothd /usr/libexec/bluetooth/bluetoothd || return 1 + + if [[ $hostonly ]]; then + # Warn user if bluetooth kernel module is loaded + # and if Peripheral (0x500) is found of minor class: + # * Keyboard (0x40) + # * Keyboard/pointing (0xC0) + # and if Appearance is set to the value defined for keyboard (0x03C1) + [ -d "/sys/class/bluetooth" ] && grep -qiE -e 'Class=0x[0-9a-f]{3}5[4c]0' -e 'Appearance=0x03c1' /var/lib/bluetooth/*/*/info 2> /dev/null \ + && dwarn "If you need to use bluetooth, please include it explicitly." + fi + + return 255 +} + +# Module dependency requirements. +depends() { + # This module has external dependencies on the systemd and dbus modules. + echo systemd dbus + # Return 0 to include the dependent modules in the initramfs. + return 0 +} + +installkernel() { + instmods bluetooth btrtl btintel btbcm bnep ath3k btusb rfcomm hidp + inst_multiple -o \ + /lib/firmware/ar3k/AthrBT* \ + /lib/firmware/ar3k/ramps* \ + /lib/firmware/ath3k-1.fw* \ + /lib/firmware/BCM2033-MD.hex* \ + /lib/firmware/bfubase.frm* \ + /lib/firmware/BT3CPCC.bin* \ + /lib/firmware/brcm/*.hcd* \ + /lib/firmware/mediatek/mt7622pr2h.bin* \ + /lib/firmware/qca/nvm* \ + /lib/firmware/qca/crnv* \ + /lib/firmware/qca/rampatch* \ + /lib/firmware/qca/crbtfw* \ + /lib/firmware/rtl_bt/* \ + /lib/firmware/intel/ibt* \ + /lib/firmware/ti-connectivity/TIInit_* \ + /lib/firmware/nokia/bcmfw.bin* \ + /lib/firmware/nokia/ti1273.bin* +} + +# Install the required file(s) for the module in the initramfs. +install() { + # shellcheck disable=SC2064 + trap "$(shopt -p globstar)" RETURN + shopt -q -s globstar + local -a var_lib_files + + inst_multiple -o \ + "$dbussystem"/bluetooth.conf \ + "$dbussystemservices"/org.bluez.service \ + "${systemdsystemunitdir}/bluetooth.target" \ + "${systemdsystemunitdir}/bluetooth.service" \ + bluetoothctl + + inst_multiple -o \ + /usr/libexec/bluetooth/bluetoothd \ + /usr/lib/bluetooth/bluetoothd + + if [[ $hostonly ]]; then + var_lib_files=("$dracutsysrootdir"/var/lib/bluetooth/**) + + inst_multiple -o \ + /etc/bluetooth/main.conf \ + "$dbussystemconfdir"/bluetooth.conf \ + "$systemdsystemconfdir"/bluetooth.service \ + "$systemdsystemconfdir/bluetooth.service.d/*.conf" \ + "${var_lib_files[@]#"$dracutsysrootdir"}" + fi + + inst_rules 69-btattach-bcm.rules 60-persistent-input.rules + + # shellcheck disable=SC1004 + sed -i -e \ + '/^\[Unit\]/aDefaultDependencies=no\ + Conflicts=shutdown.target\ + Before=shutdown.target\ + After=dbus.service' \ + "${initdir}/${systemdsystemunitdir}/bluetooth.service" + + $SYSTEMCTL -q --root "$initdir" enable bluetooth.service +} diff --git a/modules.d/80cms/cms-write-ifcfg.sh b/modules.d/80cms/cms-write-ifcfg.sh new file mode 100755 index 0000000..ecfd53e --- /dev/null +++ b/modules.d/80cms/cms-write-ifcfg.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +OLD_UMASK=$(umask) +umask 0022 +mkdir -p /run/initramfs/state/etc/sysconfig/network-scripts +umask "$OLD_UMASK" + +function cms_write_config() { + . /tmp/cms.conf + SUBCHANNELS="$(echo "$SUBCHANNELS" | sed 'y/ABCDEF/abcdef/')" + OLDIFS=$IFS + IFS=, + read -ra subch_array <<< "indexzero,$SUBCHANNELS" + IFS=$OLDIFS + devbusid=${subch_array[1]} + if [ "$NETTYPE" = "ctc" ]; then + driver="ctcm" + else + driver=$NETTYPE + fi + + DEVICE=$(cd "/sys/devices/${driver}/$devbusid/net/" && set -- * && [ "$1" != "*" ] && echo "$1") + + read -r uuid < /proc/sys/kernel/random/uuid + + IFCFGFILE=/run/initramfs/state/etc/sysconfig/network-scripts/ifcfg-$DEVICE + + strglobin "$IPADDR" '*:*:*' && ipv6=1 + + # to please NetworkManager on startup in loader before loader reconfigures net + cat > /etc/sysconfig/network << EOF +HOSTNAME=$HOSTNAME +EOF + echo "$HOSTNAME" > /etc/hostname + if [ "$ipv6" ]; then + echo "NETWORKING_IPV6=yes" >> /etc/sysconfig/network + else + echo "NETWORKING=yes" >> /etc/sysconfig/network + fi + + cat > "$IFCFGFILE" << EOF +DEVICE=$DEVICE +UUID=$uuid +ONBOOT=yes +BOOTPROTO=static +MTU=$MTU +SUBCHANNELS=$SUBCHANNELS +EOF + if [ "$ipv6" ]; then + cat >> "$IFCFGFILE" << EOF +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR=$IPADDR/$NETMASK +IPV6_DEFAULTGW=$GATEWAY +EOF + else + cat >> "$IFCFGFILE" << EOF +IPADDR=$IPADDR +NETMASK=$NETMASK +BROADCAST=$BROADCAST +GATEWAY=$GATEWAY +EOF + fi + if [ "$ipv6" ]; then + # shellcheck disable=SC2153 + IFS="," read -r DNS1 DNS2 _ <<< "$DNS" + else + IFS=":" read -r DNS1 DNS2 _ <<< "$DNS" + fi + # real DNS config for NetworkManager to generate /etc/resolv.conf + [[ $DNS1 ]] && echo "DNS1=$DNS1" >> "$IFCFGFILE" + [[ $DNS2 ]] && echo "DNS2=$DNS2" >> "$IFCFGFILE" + # just to please loader's readNetInfo && writeEnabledNetInfo + # which eats DNS1,DNS2,... and generates it themselves based on DNS + if [[ $ipv6 ]]; then + [[ $DNS ]] && echo "DNS=\"$DNS\"" >> "$IFCFGFILE" + else + [[ $DNS ]] && echo "DNS=\"${DNS/:/,}\"" >> "$IFCFGFILE" + fi + # colons in SEARCHDNS already replaced with spaces above for /etc/resolv.conf + [[ $SEARCHDNS ]] && echo "DOMAIN=\"$SEARCHDNS\"" >> "$IFCFGFILE" + [[ $NETTYPE ]] && echo "NETTYPE=$NETTYPE" >> "$IFCFGFILE" + [[ $PEERID ]] && echo "PEERID=$PEERID" >> "$IFCFGFILE" + [[ $PORTNAME ]] && echo "PORTNAME=$PORTNAME" >> "$IFCFGFILE" + [[ $CTCPROT ]] && echo "CTCPROT=$CTCPROT" >> "$IFCFGFILE" + [[ $MACADDR ]] && echo "MACADDR=$MACADDR" >> "$IFCFGFILE" + optstr="" + for option in LAYER2 PORTNO; do + [ -z "${!option}" ] && continue + [ -n "$optstr" ] && optstr=${optstr}" " + optstr=${optstr}$(echo ${option} | sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/')"="${!option} + done + # write single quotes since network.py removes double quotes but we need quotes + echo "OPTIONS='$optstr'" >> "$IFCFGFILE" + unset option + unset optstr + unset DNS1 + unset DNS2 + echo "files /etc/sysconfig/network-scripts" >> /run/initramfs/rwtab + echo "files /var/lib/dhclient" >> /run/initramfs/rwtab +} + +[ -f /tmp/cms.conf ] && cms_write_config diff --git a/modules.d/80cms/cmsifup.sh b/modules.d/80cms/cmsifup.sh new file mode 100755 index 0000000..285e20d --- /dev/null +++ b/modules.d/80cms/cmsifup.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +DEVICE=$1 + +. /tmp/cms.conf + +strglobin "$IPADDR" '*:*:*' && ipv6=1 + +if [ "$ipv6" ] && ! str_starts "$IPADDR" "["; then + IPADDR="[$IPADDR]" +fi + +if [ "$ipv6" ] && ! str_starts "$GATEWAY" "["; then + GATEWAY="[$GATEWAY]" +fi + +if [ "$ipv6" ]; then + # shellcheck disable=SC2153 + IFS="," read -r DNS1 DNS2 _ <<< "$DNS" +else + IFS=":" read -r DNS1 DNS2 _ <<< "$DNS" +fi + +{ + echo "ip=$IPADDR::$GATEWAY:$NETMASK:$HOSTNAME:$DEVICE:none:$MTU:$MACADDR" + for i in $DNS1 $DNS2; do + echo "nameserver=$i" + done +} > /etc/cmdline.d/80-cms.conf + +[ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces +IFACES="$IFACES $DEVICE" +echo "$IFACES" >> /tmp/net.ifaces + +if [ -x /usr/libexec/nm-initrd-generator ] || [ -x /usr/lib/nm-initrd-generator ]; then + type nm_generate_connections > /dev/null 2>&1 || . /lib/nm-lib.sh + nm_generate_connections + nm_reload_connections +else + exec ifup "$DEVICE" +fi diff --git a/modules.d/80cms/cmssetup.sh b/modules.d/80cms/cmssetup.sh new file mode 100755 index 0000000..68e4563 --- /dev/null +++ b/modules.d/80cms/cmssetup.sh @@ -0,0 +1,221 @@ +#!/bin/bash + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +function sysecho() { + file="$1" + shift + local i=1 + while [ $i -le 10 ]; do + if [ ! -f "$file" ]; then + sleep 1 + i=$((i + 1)) + else + break + fi + done + local status + read -r status < "$file" + if [[ $status != "$*" ]]; then + [ -f "$file" ] && echo "$*" > "$file" + fi +} + +function dasd_settle() { + local dasd_status=/sys/bus/ccw/devices/$1/status + if [ ! -f "$dasd_status" ]; then + return 1 + fi + local i=1 + while [ $i -le 60 ]; do + local status + read -r status < "$dasd_status" + case $status in + online | unformatted) + return 0 + ;; + *) + sleep 0.1 + i=$((i + 1)) + ;; + esac + done + return 1 +} + +function dasd_settle_all() { + for dasdccw in $(while read -r line || [ -n "$line" ]; do echo "${line%%(*}"; done < /proc/dasd/devices); do + if ! dasd_settle "$dasdccw"; then + echo $"Could not access DASD $dasdccw in time" + return 1 + fi + done + return 0 +} + +# prints a canonocalized device bus ID for a given devno of any format +function canonicalize_devno() { + case ${#1} in + 3) echo "0.0.0${1}" ;; + 4) echo "0.0.${1}" ;; + *) echo "${1}" ;; + esac + return 0 +} + +# read file from CMS and write it to /tmp +function readcmsfile() { # $1=dasdport $2=filename + local dev + local numcpus + local devname + local ret=0 + if [ $# -ne 2 ]; then return; fi + # precondition: udevd created dasda block device node + if ! dasd_cio_free -d "$1"; then + echo $"DASD $1 could not be cleared from device blacklist" + return 1 + fi + + modprobe dasd_mod dasd="$CMSDASD" + modprobe dasd_eckd_mod + udevadm settle + + # precondition: dasd_eckd_mod driver incl. dependencies loaded, + # dasd_mod must be loaded without setting any DASD online + dev=$(canonicalize_devno "$1") + numcpus=$( + while read -r line || [ -n "$line" ]; do + if strstr "$line" "# processors"; then + echo "${line##*:}" + break + fi + done < /proc/cpuinfo + ) + + if [ "${numcpus}" -eq 1 ]; then + echo 1 > /sys/bus/ccw/devices/"$dev"/online + else + if ! sysecho /sys/bus/ccw/devices/"$dev"/online 1; then + echo $"DASD $dev could not be set online" + return 1 + fi + udevadm settle + if ! dasd_settle "$dev"; then + echo $"Could not access DASD $dev in time" + return 1 + fi + fi + + udevadm settle + + devname=$( + cd /sys/bus/ccw/devices/"$dev"/block || exit + set -- * + [ -b /dev/"$1" ] && echo "$1" + ) + devname=${devname:-dasda} + + [[ -d /mnt ]] || mkdir -p /mnt + if cmsfs-fuse --to=UTF-8 -a /dev/"$devname" /mnt; then + cat /mnt/"$2" > /run/initramfs/"$2" + umount /mnt || umount -l /mnt + udevadm settle + else + echo $"Could not read conf file $2 on CMS DASD $1." + ret=1 + fi + + if ! sysecho /sys/bus/ccw/devices/"$dev"/online 0; then + echo $"DASD $dev could not be set offline again" + #return 1 + fi + udevadm settle + + # unbind all dasds to unload the dasd modules for a clean start + ( + cd /sys/bus/ccw/drivers/dasd-eckd || exit + for i in *.*; do echo "$i" > unbind; done + ) + udevadm settle + modprobe -r dasd_eckd_mod + udevadm settle + modprobe -r dasd_diag_mod + udevadm settle + modprobe -r dasd_mod + udevadm settle + return $ret +} + +processcmsfile() { + source /tmp/cms.conf + SUBCHANNELS="$(echo "$SUBCHANNELS" | sed 'y/ABCDEF/abcdef/')" + + if [[ $NETTYPE ]]; then + ( + echo -n "$NETTYPE","$SUBCHANNELS" + [[ $PORTNAME ]] && echo -n ",portname=$PORTNAME" + [[ $LAYER2 ]] && echo -n ",layer2=$LAYER2" + [[ $NETTYPE == "ctc" ]] && [[ $CTCPROT ]] && echo -n ",protocol=$CTCPROT" + echo + ) >> /etc/ccw.conf + + OLDIFS=$IFS + IFS=, + read -r -a subch_array <<< "indexzero,$SUBCHANNELS" + IFS=$OLDIFS + devbusid=${subch_array[1]} + if [ "$NETTYPE" = "ctc" ]; then + driver="ctcm" + else + driver=$NETTYPE + fi + + # shellcheck disable=SC2016 + printf 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="%s", KERNELS=="%s", ENV{INTERFACE}=="?*", RUN+="/sbin/initqueue --onetime --unique --name cmsifup-$name /sbin/cmsifup $name"\n' "$driver" "$devbusid" > /etc/udev/rules.d/99-cms.rules + # remove the default net rules + rm -f -- /etc/udev/rules.d/91-default-net.rules + # shellcheck disable=SC2016 + [[ -f /etc/udev/rules.d/90-net.rules ]] \ + || printf 'SUBSYSTEM=="net", ACTION=="online", RUN+="/sbin/initqueue --onetime --env netif=$name source_hook initqueue/online"\n' >> /etc/udev/rules.d/99-cms.rules + udevadm control --reload + znet_cio_free + fi + + if [[ $DASD ]] && [[ $DASD != "none" ]]; then + echo "$DASD" | normalize_dasd_arg > /etc/dasd.conf + echo "options dasd_mod dasd=$DASD" > /etc/modprobe.d/dasd_mod.conf + dasd_cio_free + fi + + unset _do_zfcp + for i in ${!FCP_*}; do + echo "${!i}" | while read -r port rest || [ -n "$port" ]; do + case $port in + *.*.*) ;; + + *.*) + port="0.$port" + ;; + *) + port="0.0.$port" + ;; + esac + echo "$port" "$rest" >> /etc/zfcp.conf + done + _do_zfcp=1 + done + [[ $_do_zfcp ]] && zfcp_cio_free + unset _do_zfcp +} + +[[ $CMSDASD ]] || CMSDASD=$(getarg "CMSDASD=") +[[ $CMSCONFFILE ]] || CMSCONFFILE=$(getarg "CMSCONFFILE=") + +# Parse configuration +if [ -n "$CMSDASD" -a -n "$CMSCONFFILE" ]; then + if readcmsfile "$CMSDASD" "$CMSCONFFILE"; then + ln -s /run/initramfs/"$CMSCONFFILE" /tmp/"$CMSCONFFILE" + ln -s /run/initramfs/"$CMSCONFFILE" /tmp/cms.conf + processcmsfile + fi +fi diff --git a/modules.d/80cms/module-setup.sh b/modules.d/80cms/module-setup.sh new file mode 100755 index 0000000..2b280e0 --- /dev/null +++ b/modules.d/80cms/module-setup.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# called by dracut +check() { + arch=${DRACUT_ARCH:-$(uname -m)} + [ "$arch" = "s390" -o "$arch" = "s390x" ] || return 1 + return 255 +} + +# called by dracut +depends() { + arch=${DRACUT_ARCH:-$(uname -m)} + [ "$arch" = "s390" -o "$arch" = "s390x" ] || return 1 + echo znet zfcp dasd dasd_mod bash + return 0 +} + +# called by dracut +installkernel() { + instmods zfcp +} + +# called by dracut +install() { + inst_hook pre-trigger 30 "$moddir/cmssetup.sh" + inst_hook pre-pivot 95 "$moddir/cms-write-ifcfg.sh" + inst_script "$moddir/cmsifup.sh" /sbin/cmsifup + # shellcheck disable=SC2046 + inst_multiple /etc/cmsfs-fuse/filetypes.conf /etc/udev/rules.d/99-fuse.rules /etc/fuse.conf \ + cmsfs-fuse fusermount bash insmod rmmod cat normalize_dasd_arg sed \ + $(rpm -ql s390utils-base) awk getopt + + inst_libdir_file "gconv/*" + #inst /usr/lib/locale/locale-archive + dracut_need_initqueue +} diff --git a/modules.d/80lvmmerge/README.md b/modules.d/80lvmmerge/README.md new file mode 100644 index 0000000..5e3e05f --- /dev/null +++ b/modules.d/80lvmmerge/README.md @@ -0,0 +1,61 @@ +# lvmmerge - dracut module + +## Preparation +- ensure that the lvm thin pool is big enough +- backup any (most likely /boot and /boot/efi) device with: +``` +# mkdir /restoredev +# dev=<device>; umount $dev; dd if="$dev" of=/restoredev/$(systemd-escape -p "$dev"); mount $dev +``` +- backup the MBR +``` +# dev=<device>; dd if="$dev" of=/restoredev/$(systemd-escape -p "$dev") bs=446 count=1 +# ls -l /dev/disk/by-path/virtio-pci-0000\:00\:07.0 +lrwxrwxrwx. 1 root root 9 Jul 24 04:27 /dev/disk/by-path/virtio-pci-0000:00:07.0 -> ../../vda +``` +- backup some partitions +``` +# dev=/dev/disk/by-path/virtio-pci-0000:00:07.0 +# dd if="$dev" of=/restoredev/$(systemd-escape -p "$dev") bs=446 count=1 +# umount /boot/efi +# dev=/dev/disk/by-partuuid/687177a8-86b3-4e37-a328-91d20db9563c +# dd if="$dev" of=/restoredev/$(systemd-escape -p "$dev") +# umount /boot +# dev=/dev/disk/by-partuuid/4fdf99e9-4f28-4207-a26f-c76546824eaf +# dd if="$dev" of=/restoredev/$(systemd-escape -p "$dev") +``` +Final /restoredev +``` +# ls -al /restoredev/ +total 1253380 +drwx------. 2 root root 250 Jul 24 04:38 . +dr-xr-xr-x. 18 root root 242 Jul 24 04:32 .. +-rw-------. 1 root root 209715200 Jul 24 04:34 dev-disk-by\x2dpartuuid-4fdf99e9\x2d4f28\x2d4207\x2da26f\x2dc76546824eaf +-rw-------. 1 root root 1073741824 Jul 24 04:34 dev-disk-by\x2dpartuuid-687177a8\x2d86b3\x2d4e37\x2da328\x2d91d20db9563c +-rw-------. 1 root root 446 Jul 24 04:38 dev-disk-by\x2dpath-virtio\x2dpci\x2d0000:00:07.0 +``` +- make a thin snapshot +``` +# lvm lvcreate -pr -s rhel/root --name reset +``` + +- mark the snapshot with a tag +``` +# lvm lvchange --addtag reset rhel/reset +``` + +- remove /restoredev +``` +# rm -fr /restoredev +``` + +## Operation + +If a boot entry with ```rd.lvm.mergetags=<tag>``` is selected and there are LVs with ```<tag>``` +dracut will +- make a copy of the snapshot +- merge it back to the original +- rename the copy back to the name of the snapshot +- if /restordev appears in the root, then it will restore the images + found in that directory. This can be used to restore /boot and /boot/efi and the + MBR of the boot device diff --git a/modules.d/80lvmmerge/lvmmerge.sh b/modules.d/80lvmmerge/lvmmerge.sh new file mode 100755 index 0000000..b8dbfa8 --- /dev/null +++ b/modules.d/80lvmmerge/lvmmerge.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +do_merge() { + sed -i -e 's/\(^[[:space:]]*\)locking_type[[:space:]]*=[[:space:]]*[[:digit:]]/\1locking_type = 1/' \ + /etc/lvm/lvm.conf + + systemctl --no-block stop sysroot.mount + swapoff -a + umount -R /sysroot + + for tag in $(getargs rd.lvm.mergetags); do + lvm vgs --noheadings -o vg_name \ + | while read -r vg || [[ -n $vg ]]; do + unset LVS + declare -a LVS + lvs=$(lvm lvs --noheadings -o lv_name "$vg") + for lv in $lvs; do + lvm lvchange -an "$vg/$lv" + + tags=$(trim "$(lvm lvs --noheadings -o lv_tags "$vg/$lv")") + strstr ",${tags}," ",${tag}," || continue + + if ! lvm lvs --noheadings -o lv_name "${vg}/${lv}_dracutsnap" &> /dev/null; then + info "Creating backup ${lv}_dracutsnap of ${vg}/${lv}" + lvm lvcreate -pr -s "${vg}/${lv}" --name "${lv}_dracutsnap" + fi + lvm lvchange --addtag "$tag" "${vg}/${lv}_dracutsnap" + + info "Merging back ${vg}/${lv} to the original LV" + lvm lvconvert --merge "${vg}/${lv}" + + LVS+=("$lv") + done + + systemctl --no-block stop sysroot.mount + udevadm settle + + for ((i = 0; i < 100; i++)); do + lvm vgchange -an "$vg" && break + sleep 0.5 + done + + udevadm settle + lvm vgchange -ay "$vg" + udevadm settle + for lv in "${LVS[@]}"; do + info "Renaming ${lv}_dracutsnap backup to ${vg}/${lv}" + lvm lvrename "$vg" "${lv}_dracutsnap" "${lv}" + done + udevadm settle + done + done + + systemctl --no-block reset-failed systemd-fsck-root + systemctl --no-block start systemd-fsck-root + systemctl --no-block reset-failed sysroot.mount + systemctl --no-block start sysroot.mount + + for ((i = 0; i < 100; i++)); do + [[ -d /sysroot/dev ]] && break + sleep 0.5 + systemctl --no-block start sysroot.mount + done + + if [[ -d /sysroot/restoredev ]]; then + ( + if cd /sysroot/restoredev; then + # restore devices and partitions + for i in *; do + target=$(systemd-escape -pu "$i") + if ! [[ -b $target ]]; then + warn "Not restoring $target, as the device does not exist" + continue + fi + + # Just in case + umount "$target" &> /dev/null + + info "Restoring $target" + dd if="$i" of="$target" |& vinfo + done + fi + ) + mount -o remount,rw /sysroot + rm -fr /sysroot/restoredev + fi + info "Rebooting" + reboot +} + +if getarg rd.lvm.mergetags; then + do_merge +fi diff --git a/modules.d/80lvmmerge/module-setup.sh b/modules.d/80lvmmerge/module-setup.sh new file mode 100755 index 0000000..c3be931 --- /dev/null +++ b/modules.d/80lvmmerge/module-setup.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# called by dracut +check() { + # No point trying to support lvm if the binaries are missing + require_binaries lvm dd swapoff || return 1 + + return 255 +} + +# called by dracut +depends() { + echo lvm dracut-systemd systemd bash + return 0 +} + +installkernel() { + hostonly="" instmods dm-snapshot +} + +# called by dracut +install() { + inst_multiple dd swapoff + inst_hook cleanup 01 "$moddir/lvmmerge.sh" +} diff --git a/modules.d/80lvmthinpool-monitor/module-setup.sh b/modules.d/80lvmthinpool-monitor/module-setup.sh new file mode 100755 index 0000000..ca015bd --- /dev/null +++ b/modules.d/80lvmthinpool-monitor/module-setup.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# called by dracut +check() { + # No point trying to support lvm if the binaries are missing + require_binaries lvm sort tr awk || return 1 + + return 255 +} + +# called by dracut +depends() { + echo lvm + return 0 +} + +# called by dracut +install() { + inst_multiple sort tr awk + inst_script "$moddir/start-thinpool-monitor.sh" "/bin/start-thinpool-monitor" + + inst "$moddir/start-thinpool-monitor.service" "$systemdsystemunitdir/start-thinpool-monitor.service" + $SYSTEMCTL -q --root "$initdir" add-wants initrd.target start-thinpool-monitor.service +} diff --git a/modules.d/80lvmthinpool-monitor/start-thinpool-monitor.service b/modules.d/80lvmthinpool-monitor/start-thinpool-monitor.service new file mode 100644 index 0000000..97f5f1f --- /dev/null +++ b/modules.d/80lvmthinpool-monitor/start-thinpool-monitor.service @@ -0,0 +1,14 @@ +[Unit] +Description=Lvm thinpool monitor service +Before=initrd.target +After=initrd-fs.target +Conflicts=shutdown.target emergency.target + +[Service] +Type=forking +ExecStart=/bin/start-thinpool-monitor +PIDFile=/run/thinpool-moni.pid +StandardInput=null +StandardOutput=journal+console +StandardError=journal+console +KillSignal=SIGHUP diff --git a/modules.d/80lvmthinpool-monitor/start-thinpool-monitor.sh b/modules.d/80lvmthinpool-monitor/start-thinpool-monitor.sh new file mode 100755 index 0000000..10f4a4b --- /dev/null +++ b/modules.d/80lvmthinpool-monitor/start-thinpool-monitor.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +LVS=$(getargs rd.lvm.lv -d rd_LVM_LV=) + +is_lvm2_thinp_device() { + _device_path=$1 + _lvm2_thin_device=$(lvm lvs -S 'lv_layout=sparse && lv_layout=thin' \ + --nosuffix --noheadings -o vg_name,lv_name "$_device_path" 2> /dev/null) + + [ -n "$_lvm2_thin_device" ] && return $? +} + +for LV in $LVS; do + if is_lvm2_thinp_device "/dev/$LV"; then + THIN_POOLS="$(lvm lvs -S 'lv_layout=sparse && lv_layout=thin' \ + --nosuffix --noheadings -o vg_name,pool_lv "$LV" \ + | awk '{printf("%s/%s",$1,$2);}') $THIN_POOLS" + fi +done + +THIN_POOLS=$(echo "$THIN_POOLS" | tr ' ' '\n' | sort -u | tr '\n' ' ') + +if [ -n "$THIN_POOLS" ]; then + if [ -e "/etc/lvm/lvm.conf" ]; then + # Use 'monitoring=0' to override the value in lvm.conf, in case + # dmeventd monitoring been started after the calling. + CONFIG="activation {monitoring=0}" + else + CONFIG="activation {monitoring=0 thin_pool_autoextend_threshold=70 thin_pool_autoextend_percent=20}" + fi + + # Activate the thinpool in case the thinpool is in inactive state. + # Otherwise lvextend will fail. + for THIN_POOL in $THIN_POOLS; do + lvm lvchange -ay "$THIN_POOL" --config "$CONFIG" + done + + while true; do + for THIN_POOL in $THIN_POOLS; do + lvm lvextend --use-policies --config "$CONFIG" "$THIN_POOL" + done + sleep 5 + done & + echo $! > /run/thinpool-moni.pid +fi diff --git a/modules.d/80test-makeroot/finished-false.sh b/modules.d/80test-makeroot/finished-false.sh new file mode 100755 index 0000000..ecdbef9 --- /dev/null +++ b/modules.d/80test-makeroot/finished-false.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exit 1 diff --git a/modules.d/80test-makeroot/module-setup.sh b/modules.d/80test-makeroot/module-setup.sh new file mode 100755 index 0000000..eb6fb74 --- /dev/null +++ b/modules.d/80test-makeroot/module-setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +check() { + # Only include the module if another module requires it + return 255 +} + +depends() { + echo "dash rootfs-block kernel-modules qemu" +} + +installkernel() { + instmods piix ide-gd_mod ata_piix ext4 sd_mod +} + +install() { + inst_multiple poweroff cp umount sync dd + inst_hook initqueue/finished 01 "$moddir/finished-false.sh" +} diff --git a/modules.d/80test-root/module-setup.sh b/modules.d/80test-root/module-setup.sh new file mode 100755 index 0000000..24605c5 --- /dev/null +++ b/modules.d/80test-root/module-setup.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +check() { + # Only include the module if another module requires it + return 255 +} + +depends() { + echo "debug" +} + +install() { + inst_simple /etc/os-release + + inst_multiple mkdir ln dd stty mount poweroff umount setsid sync + + for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do + [ -f ${_terminfodir}/l/linux ] && break + done + inst_multiple -o ${_terminfodir}/l/linux + + inst_binary "${dracutbasedir}/dracut-util" "/usr/bin/dracut-util" + ln -s dracut-util "${initdir}/usr/bin/dracut-getarg" + ln -s dracut-util "${initdir}/usr/bin/dracut-getargs" + + inst_multiple -o plymouth +} diff --git a/modules.d/80test/hard-off.sh b/modules.d/80test/hard-off.sh new file mode 100755 index 0000000..01acb19 --- /dev/null +++ b/modules.d/80test/hard-off.sh @@ -0,0 +1,3 @@ +#!/bin/sh +getargbool 0 rd.shell || poweroff -f +getargbool 0 failme && poweroff -f diff --git a/modules.d/80test/module-setup.sh b/modules.d/80test/module-setup.sh new file mode 100755 index 0000000..96041a9 --- /dev/null +++ b/modules.d/80test/module-setup.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +check() { + # Only include the module if another module requires it + return 255 +} + +depends() { + echo "debug" +} + +install() { + inst poweroff + inst_hook shutdown-emergency 000 "$moddir/hard-off.sh" + inst_hook emergency 000 "$moddir/hard-off.sh" +} diff --git a/modules.d/81cio_ignore/module-setup.sh b/modules.d/81cio_ignore/module-setup.sh new file mode 100755 index 0000000..a54fcb9 --- /dev/null +++ b/modules.d/81cio_ignore/module-setup.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +# called by dracut +check() { + # do not add this module by default + local arch=${DRACUT_ARCH:-$(uname -m)} + [ "$arch" = "s390" -o "$arch" = "s390x" ] || return 1 + return 0 +} + +cmdline() { + local cio_accept + + if [ -e /boot/zipl/active_devices.txt ]; then + while read -r dev _; do + [ "$dev" = "#" -o "$dev" = "" ] && continue + if [ -z "$cio_accept" ]; then + cio_accept="$dev" + else + cio_accept="${cio_accept},${dev}" + fi + done < /boot/zipl/active_devices.txt + fi + if [ -n "$cio_accept" ]; then + echo "rd.cio_accept=${cio_accept}" + fi +} + +# called by dracut +install() { + if [[ $hostonly_cmdline == "yes" ]]; then + local _cio_accept + _cio_accept=$(cmdline) + [[ $_cio_accept ]] && printf "%s\n" "$_cio_accept" >> "${initdir}/etc/cmdline.d/01cio_accept.conf" + fi + + inst_hook cmdline 20 "$moddir/parse-cio_accept.sh" + inst_multiple cio_ignore +} diff --git a/modules.d/81cio_ignore/parse-cio_accept.sh b/modules.d/81cio_ignore/parse-cio_accept.sh new file mode 100755 index 0000000..6cb682a --- /dev/null +++ b/modules.d/81cio_ignore/parse-cio_accept.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +CIO_IGNORE=$(getarg cio_ignore) +CIO_ACCEPT=$(getarg rd.cio_accept) + +if [ -z "$CIO_IGNORE" ]; then + info "cio_ignored disabled on commandline" + return +fi +if [ -n "$CIO_ACCEPT" ]; then + OLDIFS="$IFS" + IFS=, + # shellcheck disable=SC2086 + set -- $CIO_ACCEPT + while [ "$#" -gt 0 ]; do + info "Enabling device $1" + cio_ignore --remove "$1" + shift + done + IFS="$OLDIFS" +fi diff --git a/modules.d/90btrfs/80-btrfs.rules b/modules.d/90btrfs/80-btrfs.rules new file mode 100644 index 0000000..a2c1727 --- /dev/null +++ b/modules.d/90btrfs/80-btrfs.rules @@ -0,0 +1,8 @@ +SUBSYSTEM!="block", GOTO="btrfs_end" +ACTION!="add|change", GOTO="btrfs_end" +ENV{ID_FS_TYPE}!="btrfs", GOTO="btrfs_end" +RUN+="/sbin/btrfs device scan $env{DEVNAME}" + +RUN+="/sbin/initqueue --finished --unique --name btrfs_finished /sbin/btrfs_finished" + +LABEL="btrfs_end" diff --git a/modules.d/90btrfs/btrfs_device_ready.sh b/modules.d/90btrfs/btrfs_device_ready.sh new file mode 100755 index 0000000..93d0758 --- /dev/null +++ b/modules.d/90btrfs/btrfs_device_ready.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +btrfs_check_complete() { + local _rootinfo _dev + _dev="${1:-/dev/root}" + [ -e "$_dev" ] || return 0 + _rootinfo=$(udevadm info --query=property "--name=$_dev" 2> /dev/null) + if strstr "$_rootinfo" "ID_FS_TYPE=btrfs"; then + info "Checking, if btrfs device complete" + btrfs device ready "$_dev" > /dev/null 2>&1 + return $? + fi + return 0 +} + +btrfs_check_complete "$1" diff --git a/modules.d/90btrfs/btrfs_finished.sh b/modules.d/90btrfs/btrfs_finished.sh new file mode 100755 index 0000000..4606f8a --- /dev/null +++ b/modules.d/90btrfs/btrfs_finished.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +btrfs_check_complete() { + local _rootinfo _dev + _dev="${1:-/dev/root}" + [ -e "$_dev" ] || return 0 + _rootinfo=$(udevadm info --query=property "--name=$_dev" 2> /dev/null) + if strstr "$_rootinfo" "ID_FS_TYPE=btrfs"; then + info "Checking, if btrfs device complete" + unset __btrfs_mount + mount -o ro "$_dev" /tmp > /dev/null 2>&1 + __btrfs_mount=$? + [ $__btrfs_mount -eq 0 ] && umount "$_dev" > /dev/null 2>&1 + return $__btrfs_mount + fi + return 0 +} + +btrfs_check_complete "$1" diff --git a/modules.d/90btrfs/btrfs_timeout.sh b/modules.d/90btrfs/btrfs_timeout.sh new file mode 100755 index 0000000..326231b --- /dev/null +++ b/modules.d/90btrfs/btrfs_timeout.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +info "Scanning for all btrfs devices" +/sbin/btrfs device scan > /dev/null 2>&1 diff --git a/modules.d/90btrfs/module-setup.sh b/modules.d/90btrfs/module-setup.sh new file mode 100755 index 0000000..5d88133 --- /dev/null +++ b/modules.d/90btrfs/module-setup.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# called by dracut +check() { + # if we don't have btrfs installed on the host system, + # no point in trying to support it in the initramfs. + require_binaries btrfs || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for fs in "${host_fs_types[@]}"; do + [[ $fs == "btrfs" ]] && return 0 + done + return 255 + } + + return 0 +} + +# called by dracut +depends() { + echo udev-rules + return 0 +} + +# called by dracut +cmdline() { + # Hack for slow machines + # see https://github.com/dracutdevs/dracut/issues/658 + printf " rd.driver.pre=btrfs" +} + +# called by dracut +installkernel() { + instmods btrfs + printf "%s\n" "$(cmdline)" > "${initdir}/etc/cmdline.d/00-btrfs.conf" +} + +# called by dracut +install() { + if ! inst_rules 64-btrfs.rules; then + inst_rules "$moddir/80-btrfs.rules" + case "$(btrfs --help)" in + *device\ ready*) + inst_script "$moddir/btrfs_device_ready.sh" /sbin/btrfs_finished + ;; + *) + inst_script "$moddir/btrfs_finished.sh" /sbin/btrfs_finished + ;; + esac + else + inst_rules 64-btrfs-dm.rules + fi + + if ! dracut_module_included "systemd"; then + inst_hook initqueue/timeout 10 "$moddir/btrfs_timeout.sh" + fi + + inst_multiple -o btrfsck btrfs-zero-log + inst "$(command -v btrfs)" /sbin/btrfs +} diff --git a/modules.d/90crypt/crypt-cleanup.sh b/modules.d/90crypt/crypt-cleanup.sh new file mode 100755 index 0000000..94fa724 --- /dev/null +++ b/modules.d/90crypt/crypt-cleanup.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# close everything which is not busy +rm -f -- /etc/udev/rules.d/70-luks.rules > /dev/null 2>&1 + +if ! getarg rd.luks.uuid -d rd_LUKS_UUID > /dev/null 2>&1 && getargbool 1 rd.luks -d -n rd_NO_LUKS > /dev/null 2>&1; then + while true; do + local do_break="y" + for i in /dev/mapper/luks-*; do + cryptsetup luksClose "$i" > /dev/null 2>&1 && do_break=n + done + [ "$do_break" = "y" ] && break + done +fi diff --git a/modules.d/90crypt/crypt-lib.sh b/modules.d/90crypt/crypt-lib.sh new file mode 100755 index 0000000..f3ba20d --- /dev/null +++ b/modules.d/90crypt/crypt-lib.sh @@ -0,0 +1,278 @@ +#!/bin/sh + +command -v getarg > /dev/null || . /lib/dracut-lib.sh + +# check if the crypttab contains an entry for a LUKS UUID +crypttab_contains() { + local luks="$1" + local dev="$2" + local l d rest + if [ -f /etc/crypttab ]; then + while read -r l d rest || [ -n "$l" ]; do + strstr "${l##luks-}" "${luks##luks-}" && return 0 + strstr "$d" "${luks##luks-}" && return 0 + if [ -n "$dev" ]; then + for _dev in $(devnames "$d"); do + [ "$dev" -ef "$_dev" ] && return 0 + done + fi + if [ -e /etc/block_uuid.map ]; then + # search for line starting with $d + _line=$(sed -n "\,^$d .*$,{p}" /etc/block_uuid.map) + [ -z "$_line" ] && continue + # get second column with uuid + _uuid="$(echo "$_line" | sed 's,^.* \(.*$\),\1,')" + strstr "$_uuid" "${luks##luks-}" && return 0 + fi + done < /etc/crypttab + fi + return 1 +} + +# ask_for_password +# +# Wraps around plymouth ask-for-password and adds fallback to tty password ask +# if plymouth is not present. +# +# --cmd command +# Command to execute. Required. +# --prompt prompt +# Password prompt. Note that function already adds ':' at the end. +# Recommended. +# --tries n +# How many times repeat command on its failure. Default is 3. +# --ply-[cmd|prompt|tries] +# Command/prompt/tries specific for plymouth password ask only. +# --tty-[cmd|prompt|tries] +# Command/prompt/tries specific for tty password ask only. +# --tty-echo-off +# Turn off input echo before tty command is executed and turn on after. +# It's useful when password is read from stdin. +ask_for_password() { + local ply_cmd + local ply_prompt + local ply_tries=3 + local tty_cmd + local tty_prompt + local tty_tries=3 + local ret + + while [ $# -gt 0 ]; do + case "$1" in + --cmd) + ply_cmd="$2" + tty_cmd="$2" + shift + ;; + --ply-cmd) + ply_cmd="$2" + shift + ;; + --tty-cmd) + tty_cmd="$2" + shift + ;; + --prompt) + ply_prompt="$2" + tty_prompt="$2" + shift + ;; + --ply-prompt) + ply_prompt="$2" + shift + ;; + --tty-prompt) + tty_prompt="$2" + shift + ;; + --tries) + ply_tries="$2" + tty_tries="$2" + shift + ;; + --ply-tries) + ply_tries="$2" + shift + ;; + --tty-tries) + tty_tries="$2" + shift + ;; + --tty-echo-off) tty_echo_off=yes ;; + esac + shift + done + + { + flock -s 9 + # Prompt for password with plymouth, if installed and running. + if type plymouth > /dev/null 2>&1 && plymouth --ping 2> /dev/null; then + plymouth ask-for-password \ + --prompt "$ply_prompt" --number-of-tries="$ply_tries" \ + --command="$ply_cmd" + ret=$? + else + if [ "$tty_echo_off" = yes ]; then + stty_orig="$(stty -g)" + stty -echo + fi + + local i=1 + while [ $i -le "$tty_tries" ]; do + [ -n "$tty_prompt" ] \ + && printf "%s" "$tty_prompt [$i/$tty_tries]:" >&2 + eval "$tty_cmd" && ret=0 && break + ret=$? + i=$((i + 1)) + [ -n "$tty_prompt" ] && printf '\n' >&2 + done + + [ "$tty_echo_off" = yes ] && stty "$stty_orig" + fi + } 9> /.console_lock + + [ $ret -ne 0 ] && echo "Wrong password" >&2 + return $ret +} + +# Try to mount specified device (by path, by UUID or by label) and check +# the path with 'test'. +# +# example: +# test_dev -f LABEL="nice label" /some/file1 +test_dev() { + local test_op="$1" + local dev="$2" + local f="$3" + local ret=1 + local mount_point + + mount_point=$(mkuniqdir /mnt testdev) + [ -n "$dev" -a -n "$*" ] || return 1 + [ -d "$mount_point" ] || die 'Mount point does not exist!' + + if mount -r "$dev" "$mount_point" > /dev/null 2>&1; then + test "$test_op" "${mount_point}/${f}" + ret=$? + umount "$mount_point" + fi + + rmdir "$mount_point" + + return $ret +} + +# match_dev devpattern dev +# +# Returns true if 'dev' matches 'devpattern'. Both 'devpattern' and 'dev' are +# expanded to kernel names and then compared. If name of 'dev' is on list of +# names of devices matching 'devpattern', the test is positive. 'dev' and +# 'devpattern' may be anything which function 'devnames' recognizes. +# +# If 'devpattern' is empty or '*' then function just returns true. +# +# Example: +# match_dev UUID=123 /dev/dm-1 +# Returns true if /dev/dm-1 UUID starts with "123". +match_dev() { + [ -z "$1" -o "$1" = '*' ] && return 0 + local devlist + local dev + + devlist="$(devnames "$1")" || return 255 + dev="$(devnames "$2")" || return 255 + + strstr " +$devlist +" " +$dev +" +} + +# getkey keysfile for_dev +# +# Reads file <keysfile> produced by probe-keydev and looks for first line to +# which device <for_dev> matches. The successful result is printed in format +# "<keydev>:<keypath>". When nothing found, just false is returned. +# +# Example: +# getkey /tmp/luks.keys /dev/sdb1 +# May print: +# /dev/sdc1:/keys/some.key +getkey() { + local keys_file="$1" + local for_dev="$2" + local luks_dev + local key_dev + local key_path + + [ -z "$keys_file" -o -z "$for_dev" ] && die 'getkey: wrong usage!' + [ -f "$keys_file" ] || return 1 + + while IFS=: read -r luks_dev key_dev key_path _ || [ -n "$luks_dev" ]; do + if match_dev "$luks_dev" "$for_dev"; then + echo "${key_dev}:${key_path}" + return 0 + fi + done < "$keys_file" + + return 1 +} + +# readkey keypath keydev device +# +# Mounts <keydev>, reads key from file <keypath>, optionally processes it (e.g. +# if encrypted with GPG) and prints to standard output which is supposed to be +# read by cryptsetup. <device> is just passed to helper function for +# informational purpose. +readkey() { + local keypath="$1" + local keydev="$2" + local device="$3" + + # No mounting needed if the keyfile resides inside the initrd + if [ "/" = "$keydev" ]; then + local mntp=/ + else + # This creates a unique single mountpoint for *, or several for explicitly + # given LUKS devices. It accomplishes unlocking multiple LUKS devices with + # a single password entry. + local mntp + mntp="/mnt/$(str_replace "keydev-$keydev-$keypath" '/' '-')" + + if [ ! -d "$mntp" ]; then + mkdir -p "$mntp" + mount -r "$keydev" "$mntp" || die 'Mounting rem. dev. failed!' + fi + fi + + case "${keypath##*.}" in + gpg) + if [ -f /lib/dracut-crypt-gpg-lib.sh ]; then + . /lib/dracut-crypt-gpg-lib.sh + gpg_decrypt "$mntp" "$keypath" "$keydev" "$device" + else + die "No GPG support to decrypt '$keypath' on '$keydev'." + fi + ;; + img) + if [ -f /lib/dracut-crypt-loop-lib.sh ]; then + . /lib/dracut-crypt-loop-lib.sh + loop_decrypt "$mntp" "$keypath" "$keydev" "$device" + printf "%s\n" "umount \"$mntp\"; rmdir \"$mntp\";" > "${hookdir}/cleanup/crypt-loop-cleanup-99-${mntp##*/}".sh + return 0 + else + die "No loop file support to decrypt '$keypath' on '$keydev'." + fi + ;; + *) cat "$mntp/$keypath" ;; + esac + + # No unmounting if the keyfile resides inside the initrd + if [ "/" != "$keydev" ]; then + # General unmounting mechanism, modules doing custom cleanup should return earlier + # and install a pre-pivot cleanup hook + umount "$mntp" + rmdir "$mntp" + fi +} diff --git a/modules.d/90crypt/crypt-run-generator.sh b/modules.d/90crypt/crypt-run-generator.sh new file mode 100755 index 0000000..3e78e6d --- /dev/null +++ b/modules.d/90crypt/crypt-run-generator.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +. /lib/dracut-lib.sh +type crypttab_contains > /dev/null 2>&1 || . /lib/dracut-crypt-lib.sh + +dev=$1 +luks=$2 + +crypttab_contains "$luks" "$dev" && exit 0 + +allowdiscards="-" + +# parse for allow-discards +if [ -n "$DRACUT_SYSTEMD" ] || strstr "$(cryptsetup --help)" "allow-discards"; then + if discarduuids=$(getargs "rd.luks.allow-discards"); then + discarduuids=$(str_replace "$discarduuids" 'luks-' '') + if strstr " $discarduuids " " ${luks##luks-}"; then + allowdiscards="discard" + fi + elif getargbool 0 rd.luks.allow-discards; then + allowdiscards="discard" + fi +fi + +echo "$luks $dev - timeout=0,$allowdiscards" >> /etc/crypttab + +if command -v systemctl > /dev/null; then + systemctl daemon-reload + systemctl start cryptsetup.target +fi +exit 0 diff --git a/modules.d/90crypt/cryptroot-ask.sh b/modules.d/90crypt/cryptroot-ask.sh new file mode 100755 index 0000000..b1f8df8 --- /dev/null +++ b/modules.d/90crypt/cryptroot-ask.sh @@ -0,0 +1,207 @@ +#!/bin/sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin +NEWROOT=${NEWROOT:-"/sysroot"} + +# do not ask, if we already have root +[ -f "$NEWROOT"/proc ] && exit 0 + +. /lib/dracut-lib.sh + +mkdir -p -m 0700 /run/cryptsetup + +# if device name is /dev/dm-X, convert to /dev/mapper/name +if [ "${1##/dev/dm-}" != "$1" ]; then + device="/dev/mapper/$(dmsetup info -c --noheadings -o name "$1")" +else + device="$1" +fi + +# default luksname - luks-UUID +luksname=$2 + +# is_keysource - ask for passphrase even if a rd.luks.key argument is set +is_keysource=${3:-0} + +# number of tries +numtries=${4:-10} + +# TODO: improve to support what cmdline does +if [ -f /etc/crypttab ] && getargbool 1 rd.luks.crypttab -d -n rd_NO_CRYPTTAB; then + while read -r name dev luksfile luksoptions || [ -n "$name" ]; do + # ignore blank lines and comments + if [ -z "$name" -o "${name#\#}" != "$name" ]; then + continue + fi + + # PARTUUID used in crypttab + if [ "${dev%%=*}" = "PARTUUID" ]; then + if [ "luks-${dev##PARTUUID=}" = "$luksname" ]; then + luksname="$name" + break + fi + + # UUID used in crypttab + elif [ "${dev%%=*}" = "UUID" ]; then + if [ "luks-${dev##UUID=}" = "$luksname" ]; then + luksname="$name" + break + fi + + # ID used in crypttab + elif [ "${dev%%=*}" = "ID" ]; then + if [ "luks-${dev##ID=}" = "$luksname" ]; then + luksname="$name" + break + fi + + # path used in crypttab + else + cdev=$(readlink -f "$dev") + mdev=$(readlink -f "$device") + if [ "$cdev" = "$mdev" ]; then + luksname="$name" + break + fi + fi + done < /etc/crypttab + unset name dev +fi + +# check if destination already exists +[ -b /dev/mapper/"$luksname" ] && exit 0 + +# we already asked for this device +asked_file=/tmp/cryptroot-asked-$luksname +[ -f "$asked_file" ] && exit 0 + +# load dm_crypt if it is not already loaded +[ -d /sys/module/dm_crypt ] || modprobe dm_crypt + +. /lib/dracut-crypt-lib.sh + +# +# Open LUKS device +# + +info "luksOpen $device $luksname $luksfile $luksoptions" + +OLD_IFS="$IFS" +IFS=, +# shellcheck disable=SC2086 +set -- $luksoptions +IFS="$OLD_IFS" + +while [ $# -gt 0 ]; do + case $1 in + noauto) + # skip this + exit 0 + ;; + swap) + # skip this + exit 0 + ;; + tmp) + # skip this + exit 0 + ;; + allow-discards) + allowdiscards="--allow-discards" + ;; + header=*) + cryptsetupopts="${cryptsetupopts} --${1}" + ;; + esac + shift +done + +# parse for allow-discards +if strstr "$(cryptsetup --help)" "allow-discards"; then + if discarduuids=$(getargs "rd.luks.allow-discards"); then + discarduuids=$(str_replace "$discarduuids" 'luks-' '') + if strstr " $discarduuids " " ${luksdev##luks-}"; then + allowdiscards="--allow-discards" + fi + elif getargbool 0 rd.luks.allow-discards; then + allowdiscards="--allow-discards" + fi +fi + +if strstr "$(cryptsetup --help)" "allow-discards"; then + cryptsetupopts="$cryptsetupopts $allowdiscards" +fi + +unset allowdiscards + +# fallback to passphrase +ask_passphrase=1 + +if [ -n "$luksfile" -a "$luksfile" != "none" -a -e "$luksfile" ]; then + # shellcheck disable=SC2086 + if readkey "$luksfile" / "$device" \ + | cryptsetup -d - $cryptsetupopts luksOpen "$device" "$luksname"; then + ask_passphrase=0 + fi +elif [ "$is_keysource" -ne 0 ]; then + info "Asking for passphrase because $device is a keysource." +else + while [ -n "$(getarg rd.luks.key)" ]; do + if tmp=$(getkey /tmp/luks.keys "$device"); then + keydev="${tmp%%:*}" + keypath="${tmp#*:}" + else + if [ "$numtries" -eq 0 ]; then + warn "No key found for $device. Fallback to passphrase mode." + break + fi + sleep 1 + info "No key found for $device. Will try $numtries time(s) more later." + initqueue --unique --onetime --settled \ + --name cryptroot-ask-"$luksname" \ + "$(command -v cryptroot-ask)" "$device" "$luksname" "$is_keysource" "$((numtries - 1))" + exit 0 + fi + unset tmp + + info "Using '$keypath' on '$keydev'" + # shellcheck disable=SC2086 + readkey "$keypath" "$keydev" "$device" \ + | cryptsetup -d - $cryptsetupopts luksOpen "$device" "$luksname" \ + && ask_passphrase=0 + unset keypath keydev + break + done +fi + +if [ $ask_passphrase -ne 0 ]; then + luks_open="$(command -v cryptsetup) $cryptsetupopts luksOpen" + _timeout=$(getargs "rd.luks.timeout") + _timeout=${_timeout:-0} + ask_for_password --ply-tries 5 \ + --ply-cmd "$luks_open -T1 $device $luksname" \ + --ply-prompt "Password ($device)" \ + --tty-tries 1 \ + --tty-cmd "$luks_open -T5 -t $_timeout $device $luksname" + unset luks_open + unset _timeout +fi + +if [ "$is_keysource" -ne 0 -a "${luksname##luks-}" != "$luksname" ]; then + luks_close="$(command -v cryptsetup) close" + { + printf -- '[ -e /dev/mapper/%s ] && ' "$luksname" + printf -- '%s "%s"\n' "$luks_close" "$luksname" + } >> "$hookdir/cleanup/31-crypt-keysource.sh" + unset luks_close +fi + +unset device luksname luksfile + +# mark device as asked +: >> "$asked_file" + +need_shutdown +udevsettle + +exit 0 diff --git a/modules.d/90crypt/module-setup.sh b/modules.d/90crypt/module-setup.sh new file mode 100755 index 0000000..d5ac45b --- /dev/null +++ b/modules.d/90crypt/module-setup.sh @@ -0,0 +1,186 @@ +#!/bin/bash + +# called by dracut +check() { + local fs + # if cryptsetup is not installed, then we cannot support encrypted devices. + require_any_binary "$systemdutildir"/systemd-cryptsetup cryptsetup || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for fs in "${host_fs_types[@]}"; do + [[ $fs == "crypto_LUKS" ]] && return 0 + done + return 255 + } + + return 0 +} + +# called by dracut +depends() { + local deps + deps="dm rootfs-block" + if [[ $hostonly && -f "$dracutsysrootdir"/etc/crypttab ]]; then + if grep -q -e "fido2-device=" -e "fido2-cid=" "$dracutsysrootdir"/etc/crypttab; then + deps+=" fido2" + fi + if grep -q "pkcs11-uri" "$dracutsysrootdir"/etc/crypttab; then + deps+=" pkcs11" + fi + if grep -q "tpm2-device=" "$dracutsysrootdir"/etc/crypttab; then + deps+=" tpm2-tss" + fi + fi + echo "$deps" + return 0 +} + +# called by dracut +installkernel() { + hostonly="" instmods drbg + instmods dm_crypt + + # in case some of the crypto modules moved from compiled in + # to module based, try to install those modules + # best guess + if [[ $hostonly ]] || [[ $mount_needs ]]; then + # dmsetup returns s.th. like + # cryptvol: 0 2064384 crypt aes-xts-plain64 :64:logon:cryptsetup:.... + dmsetup table | while read -r name _ _ is_crypt cipher _; do + [[ $is_crypt == "crypt" ]] || continue + # get the device name + name=/dev/$(dmsetup info -c --noheadings -o blkdevname "${name%:}") + # check if the device exists as a key in our host_fs_types (even with null string) + # shellcheck disable=SC2030 # this is a shellcheck bug + if [[ ${host_fs_types[$name]+_} ]]; then + # split the cipher aes-xts-plain64 in pieces + IFS='-:' read -ra mods <<< "$cipher" + # try to load the cipher part with "crypto-" prepended + # in non-hostonly mode + hostonly='' instmods "${mods[@]/#/crypto-}" "crypto-$cipher" + fi + done + else + instmods "=crypto" + fi + return 0 +} + +# called by dracut +cmdline() { + local dev UUID + # shellcheck disable=SC2031 + for dev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$dev]} != "crypto_LUKS" ]] && continue + + UUID=$( + blkid -u crypto -o export "$dev" \ + | while read -r line || [ -n "$line" ]; do + [[ ${line#UUID} == "$line" ]] && continue + printf "%s" "${line#UUID=}" + break + done + ) + [[ ${UUID} ]] || continue + printf "%s" " rd.luks.uuid=luks-${UUID}" + done +} + +# called by dracut +install() { + + if [[ $hostonly_cmdline == "yes" ]]; then + local _cryptconf + _cryptconf=$(cmdline) + [[ $_cryptconf ]] && printf "%s\n" "$_cryptconf" >> "${initdir}/etc/cmdline.d/90crypt.conf" + fi + + inst_hook cmdline 30 "$moddir/parse-crypt.sh" + if ! dracut_module_included "systemd"; then + inst_multiple cryptsetup rmdir readlink umount + inst_script "$moddir"/cryptroot-ask.sh /sbin/cryptroot-ask + inst_script "$moddir"/probe-keydev.sh /sbin/probe-keydev + inst_hook cmdline 10 "$moddir/parse-keydev.sh" + inst_hook cleanup 30 "$moddir/crypt-cleanup.sh" + fi + + if [[ $hostonly ]] && [[ -f $dracutsysrootdir/etc/crypttab ]]; then + # filter /etc/crypttab for the devices we need + while read -r _mapper _dev _luksfile _luksoptions || [ -n "$_mapper" ]; do + [[ $_mapper == \#* ]] && continue + [[ $_dev ]] || continue + + [[ $_dev == PARTUUID=* ]] \ + && _dev="/dev/disk/by-partuuid/${_dev#PARTUUID=}" + + [[ $_dev == UUID=* ]] \ + && _dev="/dev/disk/by-uuid/${_dev#UUID=}" + + [[ $_dev == ID=* ]] \ + && _dev="/dev/disk/by-id/${_dev#ID=}" + + echo "$_dev $(blkid "$_dev" -s UUID -o value)" >> "${initdir}/etc/block_uuid.map" + + # loop through the options to check for the force option + luksoptions=${_luksoptions} + OLD_IFS="${IFS}" + IFS=, + # shellcheck disable=SC2086 + set -- ${luksoptions} + IFS="${OLD_IFS}" + + forceentry="" + while [ $# -gt 0 ]; do + case $1 in + force) + forceentry="yes" + break + ;; + esac + shift + done + + # include the entry regardless + if [ "${forceentry}" = "yes" ]; then + echo "$_mapper $_dev $_luksfile $_luksoptions" + else + # shellcheck disable=SC2031 + for _hdev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$_hdev]} == "crypto_LUKS" ]] || continue + if [[ $_hdev -ef $_dev ]] || [[ /dev/block/$_hdev -ef $_dev ]]; then + echo "$_mapper $_dev $_luksfile $_luksoptions" + break + fi + done + fi + done < "$dracutsysrootdir"/etc/crypttab > "$initdir"/etc/crypttab + mark_hostonly /etc/crypttab + fi + + inst_simple "$moddir/crypt-lib.sh" "/lib/dracut-crypt-lib.sh" + inst_script "$moddir/crypt-run-generator.sh" "/sbin/crypt-run-generator" + + if dracut_module_included "systemd"; then + # the cryptsetup targets are already pulled in by 00systemd, but not + # the enablement symlinks + inst_multiple -o \ + "$tmpfilesdir"/cryptsetup.conf \ + "$systemdutildir"/system-generators/systemd-cryptsetup-generator \ + "$systemdutildir"/systemd-cryptsetup \ + "$systemdsystemunitdir"/systemd-ask-password-console.path \ + "$systemdsystemunitdir"/systemd-ask-password-console.service \ + "$systemdsystemunitdir"/cryptsetup.target \ + "$systemdsystemunitdir"/sysinit.target.wants/cryptsetup.target \ + "$systemdsystemunitdir"/remote-cryptsetup.target \ + "$systemdsystemunitdir"/initrd-root-device.target.wants/remote-cryptsetup.target \ + systemd-ask-password systemd-tty-ask-password-agent + fi + + # Install required libraries. + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file \ + {"tls/$_arch/",tls/,"$_arch/",}"/ossl-modules/fips.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"/ossl-modules/legacy.so" + + dracut_need_initqueue +} diff --git a/modules.d/90crypt/parse-crypt.sh b/modules.d/90crypt/parse-crypt.sh new file mode 100755 index 0000000..e46e347 --- /dev/null +++ b/modules.d/90crypt/parse-crypt.sh @@ -0,0 +1,197 @@ +#!/bin/sh + +type crypttab_contains > /dev/null 2>&1 || . /lib/dracut-crypt-lib.sh + +_cryptgetargsname() { + debug_off + local _o _found _key + unset _o + unset _found + _key="$1" + set -- + for _o in $(getargs rd.luks.name); do + if [ "${_o%=*}" = "${_key%=}" ]; then + [ -n "${_o%=*}" ] && set -- "$@" "${_o#*=}" + _found=1 + fi + done + if [ -n "$_found" ]; then + [ $# -gt 0 ] && printf '%s' "$*" + return 0 + fi + return 1 +} + +if ! getargbool 1 rd.luks -d -n rd_NO_LUKS; then + info "rd.luks=0: removing cryptoluks activation" + rm -f -- /etc/udev/rules.d/70-luks.rules +else + { + echo 'SUBSYSTEM!="block", GOTO="luks_end"' + echo 'ACTION!="add|change", GOTO="luks_end"' + } > /etc/udev/rules.d/70-luks.rules.new + + PARTUUID=$(getargs rd.luks.partuuid -d rd_LUKS_PARTUUID) + SERIAL=$(getargs rd.luks.serial -d rd_LUKS_SERIAL) + LUKS=$(getargs rd.luks.uuid -d rd_LUKS_UUID) + tout=$(getarg rd.luks.key.tout) + + if [ -e /etc/crypttab ]; then + while read -r _ _dev _ || [ -n "$_dev" ]; do + set_systemd_timeout_for_dev "$_dev" + done < /etc/crypttab + fi + + if [ -n "$PARTUUID" ]; then + for uuid in $PARTUUID; do + + is_keysource=0 + _uuid=$uuid + uuid=${uuid#keysource:} + [ "$uuid" != "$_uuid" ] && is_keysource=1 + unset _uuid + + uuid=${uuid##luks-} + if luksname=$(_cryptgetargsname "$uuid="); then + luksname="${luksname#"$uuid"=}" + else + luksname="luks-$uuid" + fi + + if [ -z "$DRACUT_SYSTEMD" ]; then + { + printf -- 'ENV{ID_PART_ENTRY_UUID}=="*%s*", ' "$uuid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)" + # shellcheck disable=SC2016 + printf -- '$env{DEVNAME} %s %s %s"\n' "$luksname" "$is_keysource" "$tout" + } >> /etc/udev/rules.d/70-luks.rules.new + else + luksname=$(dev_unit_name "$luksname") + # shellcheck disable=SC1003 + luksname="$(str_replace "$luksname" '\' '\\')" + + if ! crypttab_contains "$uuid"; then + { + printf -- 'ENV{ID_PART_ENTRY_UUID}=="*%s*", ' "$uuid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name systemd-cryptsetup-%%k %s start ' "$(command -v systemctl)" + printf -- 'systemd-cryptsetup@%s.service"\n' "$luksname" + } >> /etc/udev/rules.d/70-luks.rules.new + fi + fi + done + + elif [ -n "$SERIAL" ]; then + for serialid in $SERIAL; do + + is_keysource=0 + _serialid=$serialid + serialid=${serialid#keysource:} + [ "$serialid" != "$_serialid" ] && is_keysource=1 + unset _serialid + + serialid=${serialid##luks-} + if luksname=$(_cryptgetargsname "$serialid="); then + luksname="${luksname#"$serialid"=}" + else + luksname="luks-$serialid" + fi + + if [ -z "$DRACUT_SYSTEMD" ]; then + { + printf -- 'ENV{ID_SERIAL_SHORT}=="*%s*", ' "$serialid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)" + # shellcheck disable=SC2016 + printf -- '$env{DEVNAME} %s %s %s"\n' "$luksname" "$is_keysource" "$tout" + } >> /etc/udev/rules.d/70-luks.rules.new + else + luksname=$(dev_unit_name "$luksname") + # shellcheck disable=SC1003 + luksname="$(str_replace "$luksname" '\' '\\')" + + if ! crypttab_contains "$serialid"; then + { + printf -- 'ENV{ID_SERIAL_SHORT}=="*%s*", ' "$serialid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name systemd-cryptsetup-%%k %s start ' "$(command -v systemctl)" + printf -- 'systemd-cryptsetup@%s.service"\n' "$luksname" + } >> /etc/udev/rules.d/70-luks.rules.new + fi + fi + done + + elif [ -n "$LUKS" ]; then + for luksid in $LUKS; do + + is_keysource=0 + _luksid=$luksid + luksid=${luksid#keysource:} + [ "$luksid" != "$_luksid" ] && is_keysource=1 + unset _luksid + + luksid=${luksid##luks-} + if luksname=$(_cryptgetargsname "$luksid="); then + luksname="${luksname#"$luksid"=}" + else + luksname="luks-$luksid" + fi + + if [ -z "$DRACUT_SYSTEMD" ]; then + { + printf -- 'ENV{ID_FS_TYPE}=="crypto_LUKS", ' + printf -- 'ENV{ID_FS_UUID}=="*%s*", ' "$luksid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)" + # shellcheck disable=SC2016 + printf -- '$env{DEVNAME} %s %s %s"\n' "$luksname" "$is_keysource" "$tout" + } >> /etc/udev/rules.d/70-luks.rules.new + else + luksname=$(dev_unit_name "$luksname") + # shellcheck disable=SC1003 + luksname="$(str_replace "$luksname" '\' '\\')" + + if ! crypttab_contains "$luksid"; then + { + printf -- 'ENV{ID_FS_TYPE}=="crypto_LUKS", ' + printf -- 'ENV{ID_FS_UUID}=="*%s*", ' "$luksid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name systemd-cryptsetup-%%k %s start ' "$(command -v systemctl)" + printf -- 'systemd-cryptsetup@%s.service"\n' "$luksname" + } >> /etc/udev/rules.d/70-luks.rules.new + fi + fi + + if [ $is_keysource -eq 0 ]; then + uuid=$luksid + while [ "$uuid" != "${uuid#*-}" ]; do uuid=${uuid%%-*}${uuid#*-}; done + printf -- '[ -e /dev/disk/by-id/dm-uuid-CRYPT-LUKS?-*%s*-* ] || exit 1\n' "$uuid" \ + >> "$hookdir/initqueue/finished/90-crypt.sh" + { + printf -- '[ -e /dev/disk/by-uuid/*%s* ] || ' "$luksid" + printf -- 'warn "crypto LUKS UUID "%s" not found"\n' "$luksid" + } >> "$hookdir/emergency/90-crypt.sh" + fi + done + elif getargbool 0 rd.auto; then + if [ -z "$DRACUT_SYSTEMD" ]; then + { + printf -- 'ENV{ID_FS_TYPE}=="crypto_LUKS", RUN+="%s ' "$(command -v initqueue)" + printf -- '--unique --settled --onetime --name cryptroot-ask-%%k ' + # shellcheck disable=SC2016 + printf -- '%s $env{DEVNAME} luks-$env{ID_FS_UUID} 0 %s"\n' "$(command -v cryptroot-ask)" "$tout" + } >> /etc/udev/rules.d/70-luks.rules.new + else + { + printf -- 'ENV{ID_FS_TYPE}=="crypto_LUKS", RUN+="%s ' "$(command -v initqueue)" + printf -- '--unique --settled --onetime --name crypt-run-generator-%%k ' + # shellcheck disable=SC2016 + printf -- '%s $env{DEVNAME} luks-$env{ID_FS_UUID}"\n' "$(command -v crypt-run-generator)" + } >> /etc/udev/rules.d/70-luks.rules.new + fi + fi + + echo 'LABEL="luks_end"' >> /etc/udev/rules.d/70-luks.rules.new + mv /etc/udev/rules.d/70-luks.rules.new /etc/udev/rules.d/70-luks.rules +fi diff --git a/modules.d/90crypt/parse-keydev.sh b/modules.d/90crypt/parse-keydev.sh new file mode 100755 index 0000000..467d892 --- /dev/null +++ b/modules.d/90crypt/parse-keydev.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +if getargbool 1 rd.luks -n rd_NO_LUKS \ + && [ -n "$(getarg rd.luks.key)" ]; then + exec 7> /etc/udev/rules.d/65-luks-keydev.rules + echo 'SUBSYSTEM!="block", GOTO="luks_keydev_end"' >&7 + echo 'ACTION!="add|change", GOTO="luks_keydev_end"' >&7 + + for arg in $(getargs rd.luks.key); do + unset keypath keydev luksdev + splitsep : "$arg" keypath keydev luksdev + + info "rd.luks.key: keypath='$keypath' keydev='$keydev' luksdev='$luksdev'" + + if [ -z "$keypath" ]; then + warn 'keypath required!' + continue + fi + + # A keydev of '/' is treated as the initrd itself + if [ "/" = "$keydev" ]; then + [ -z "$luksdev" ] && luksdev='*' + echo "$luksdev:$keydev:$keypath" >> /tmp/luks.keys + continue + elif [ -n "$keydev" ]; then + udevmatch "$keydev" >&7 || { + warn 'keydev incorrect!' + continue + } + printf ', ' >&7 + fi + + { + printf -- 'RUN+="%s --unique --onetime ' "$(command -v initqueue)" + printf -- '--name probe-keydev-%%k ' + printf -- '%s /dev/%%k %s %s"\n' \ + "$(command -v probe-keydev)" "${keypath}" "${luksdev}" + } >&7 + done + unset arg keypath keydev luksdev + + echo 'LABEL="luks_keydev_end"' >&7 + exec 7>&- +fi diff --git a/modules.d/90crypt/probe-keydev.sh b/modules.d/90crypt/probe-keydev.sh new file mode 100755 index 0000000..e5a3f36 --- /dev/null +++ b/modules.d/90crypt/probe-keydev.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +. /lib/dracut-crypt-lib.sh + +real_keydev="$1" +keypath="$2" +luksdev="$3" + +[ -z "$real_keydev" -o -z "$keypath" ] && die 'probe-keydev: wrong usage!' +[ -z "$luksdev" ] && luksdev='*' + +info "Probing $real_keydev for $keypath..." +test_dev -f "$real_keydev" "$keypath" || exit 1 + +info "Found $keypath on $real_keydev" +echo "$luksdev:$real_keydev:$keypath" >> /tmp/luks.keys diff --git a/modules.d/90dm/11-dm.rules b/modules.d/90dm/11-dm.rules new file mode 100644 index 0000000..89941c4 --- /dev/null +++ b/modules.d/90dm/11-dm.rules @@ -0,0 +1,5 @@ +SUBSYSTEM!="block", GOTO="dm_end" +KERNEL!="dm-[0-9]*", GOTO="dm_end" +ACTION!="add|change", GOTO="dm_end" +OPTIONS+="db_persist" +LABEL="dm_end" diff --git a/modules.d/90dm/59-persistent-storage-dm.rules b/modules.d/90dm/59-persistent-storage-dm.rules new file mode 100644 index 0000000..3e0b8f6 --- /dev/null +++ b/modules.d/90dm/59-persistent-storage-dm.rules @@ -0,0 +1,15 @@ +SUBSYSTEM!="block", GOTO="dm_end" +ACTION!="add|change", GOTO="dm_end" +# Also don't process disks that are slated to be a multipath device +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="dm_end" + +KERNEL!="dm-[0-9]*", GOTO="dm_end" +ACTION=="add", GOTO="dm_end" +IMPORT{program}="/sbin/dmsetup info -c --nameprefixes --unquoted --rows --noheadings -o name,uuid,suspended,readonly,major,minor,open,tables_loaded,names_using_dev -j%M -m%m" +ENV{DM_NAME}!="?*", GOTO="dm_end" +ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", GOTO="dm_end" +ENV{DM_UUID}=="CRYPT-TEMP-?*", GOTO="dm_end" +ENV{DM_UUID}!="?*", ENV{DM_NAME}=="temporary-cryptsetup-?*", GOTO="dm_end" +IMPORT{builtin}="blkid" + +LABEL="dm_end" diff --git a/modules.d/90dm/dm-pre-udev.sh b/modules.d/90dm/dm-pre-udev.sh new file mode 100755 index 0000000..ba8d962 --- /dev/null +++ b/modules.d/90dm/dm-pre-udev.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +strstr "$(cat /proc/misc)" device-mapper || modprobe dm_mod +modprobe dm_mirror 2> /dev/null diff --git a/modules.d/90dm/dm-shutdown.sh b/modules.d/90dm/dm-shutdown.sh new file mode 100755 index 0000000..bd7134a --- /dev/null +++ b/modules.d/90dm/dm-shutdown.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +_remove_dm() { + local dev="$1" + local s + local devname + + for s in /sys/block/"${dev}"/holders/dm-*; do + [ -e "${s}" ] || continue + _remove_dm "${s##*/}" + done + # multipath devices might have MD devices on top, + # which are removed after this script. So do not + # remove those to avoid spurious errors + case $(cat /sys/block/"${dev}"/dm/uuid) in + mpath-*) + return 0 + ;; + *) + read -r devname < /sys/block/"${dev}"/dm/name + dmsetup -v --noudevsync remove "$devname" || return $? + ;; + esac + return 0 +} + +_do_dm_shutdown() { + local ret=0 + local final="$1" + local dev + + info "Disassembling device-mapper devices" + for dev in /sys/block/dm-*; do + [ -e "${dev}" ] || continue + if [ "x$final" != "x" ]; then + _remove_dm "${dev##*/}" || ret=$? + else + _remove_dm "${dev##*/}" > /dev/null 2>&1 || ret=$? + fi + done + if [ "x$final" != "x" ]; then + info "dmsetup ls --tree" + dmsetup ls --tree 2>&1 | vinfo + fi + return $ret +} + +if command -v dmsetup > /dev/null \ + && [ "x$(dmsetup status)" != "xNo devices found" ]; then + _do_dm_shutdown "$1" +else + : +fi diff --git a/modules.d/90dm/module-setup.sh b/modules.d/90dm/module-setup.sh new file mode 100755 index 0000000..0c4cba3 --- /dev/null +++ b/modules.d/90dm/module-setup.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# called by dracut +check() { + require_binaries dmsetup || return 1 + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + instmods '=drivers/md' dm_mod dm-cache dm-cache-mq dm-cache-cleaner +} + +# called by dracut +install() { + modinfo -k "$kernel" dm_mod > /dev/null 2>&1 \ + && inst_hook pre-udev 30 "$moddir/dm-pre-udev.sh" + + inst_multiple dmsetup + + inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules + # Gentoo ebuild for LVM2 prior to 2.02.63-r1 doesn't install above rules + # files, but provides the one below: + inst_rules 64-device-mapper.rules + # debian udev rules + inst_rules 60-persistent-storage-dm.rules 55-dm.rules + + inst_rules "$moddir/11-dm.rules" + + inst_rules "$moddir/59-persistent-storage-dm.rules" + + inst_hook shutdown 25 "$moddir/dm-shutdown.sh" +} diff --git a/modules.d/90dmraid/61-dmraid-imsm.rules b/modules.d/90dmraid/61-dmraid-imsm.rules new file mode 100644 index 0000000..8a6b215 --- /dev/null +++ b/modules.d/90dmraid/61-dmraid-imsm.rules @@ -0,0 +1,28 @@ +# This file causes block devices with RAID (dmraid) signatures to +# automatically cause dmraid_scan to be run. +# See udev(8) for syntax + +SUBSYSTEM!="block", GOTO="dm_end" +ACTION!="add|change", GOTO="dm_end" +# Also don't process disks that are slated to be a multipath device +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="dm_end" + +ENV{ID_FS_TYPE}=="linux_raid_member", GOTO="dm_end" + +ENV{ID_FS_TYPE}!="*_raid_member", GOTO="dm_end" + +ENV{ID_FS_TYPE}=="isw_raid_member", ENV{rd_NO_MDIMSM}!="?*", GOTO="dm_end" +ENV{ID_FS_TYPE}=="ddf_raid_member", ENV{rd_NO_MDDDF}!="?*", GOTO="dm_end" + +ENV{rd_NO_DM}=="?*", GOTO="dm_end" + +OPTIONS:="nowatch" + +ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", GOTO="dm_end" + +PROGRAM=="/bin/sh -c 'for i in $sys/$devpath/holders/dm-[0-9]*; do [ -e $$i ] && exit 0; done; exit 1;' ", \ + GOTO="dm_end" + +RUN+="/sbin/initqueue --onetime --unique --settled /sbin/dmraid_scan $env{DEVNAME}" + +LABEL="dm_end" diff --git a/modules.d/90dmraid/dmraid.sh b/modules.d/90dmraid/dmraid.sh new file mode 100755 index 0000000..b517320 --- /dev/null +++ b/modules.d/90dmraid/dmraid.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +devenc=$(str_replace "$1" '/' '\2f') + +[ -e /tmp/dmraid."$devenc" ] && exit 0 + +: > /tmp/dmraid."$devenc" + +DM_RAIDS=$(getargs rd.dm.uuid -d rd_DM_UUID=) + +if [ -n "$DM_RAIDS" ] || getargbool 0 rd.auto; then + # run dmraid if udev has settled + info "Scanning for dmraid devices $DM_RAIDS" + SETS=$(dmraid -c -s) + + if [ "$SETS" = "no raid disks" -o "$SETS" = "no raid sets" ]; then + return + fi + + info "Found dmraid sets:" + echo "$SETS" | vinfo + + if [ -n "$DM_RAIDS" ]; then + # only activate specified DM RAIDS + for r in $DM_RAIDS; do + for s in $SETS; do + if [ "${s##"$r"}" != "$s" ]; then + info "Activating $s" + dmraid -ay -i -p --rm_partitions "$s" 2>&1 | vinfo + fi + done + done + else + # scan and activate all DM RAIDS + for s in $SETS; do + info "Activating $s" + dmraid -ay -i -p --rm_partitions "$s" 2>&1 | vinfo + [ -e "/dev/mapper/$s" ] && kpartx -a "/dev/mapper/$s" 2>&1 | vinfo + udevsettle + done + fi + + need_shutdown +fi diff --git a/modules.d/90dmraid/module-setup.sh b/modules.d/90dmraid/module-setup.sh new file mode 100755 index 0000000..482ae96 --- /dev/null +++ b/modules.d/90dmraid/module-setup.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# called by dracut +check() { + local holder + local dev + + # if we don't have dmraid installed on the host system, no point + # in trying to support it in the initramfs. + require_binaries dmraid || return 1 + require_binaries kpartx || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for dev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$dev]} != *_raid_member ]] && continue + + DEVPATH=$(get_devpath_block "$dev") + + for holder in "$DEVPATH"/holders/*; do + [[ -e $holder ]] || continue + [[ -e "$holder/dm" ]] && return 0 + break + done + + done + return 255 + } + + return 0 +} + +# called by dracut +depends() { + echo dm rootfs-block + return 0 +} + +# called by dracut +cmdline() { + local dev + local -A _activated + + for dev in "${!host_fs_types[@]}"; do + local holder DEVPATH DM_NAME + [[ ${host_fs_types[$dev]} != *_raid_member ]] && continue + + DEVPATH=$(get_devpath_block "$dev") + + for holder in "$DEVPATH"/holders/*; do + [[ -e $holder ]] || continue + dev="/dev/${holder##*/}" + DM_NAME="$(dmsetup info -c --noheadings -o name "$dev" 2> /dev/null)" + [[ ${DM_NAME} ]] && break + done + + [[ ${DM_NAME} ]] || continue + + if ! [[ ${_activated[${DM_NAME}]} ]]; then + printf "%s" " rd.dm.uuid=${DM_NAME}" + _activated["${DM_NAME}"]=1 + fi + done +} + +# called by dracut +install() { + local _raidconf + + if [[ $hostonly_cmdline == "yes" ]]; then + _raidconf=$(cmdline) + [[ $_raidconf ]] && printf "%s\n" "$_raidconf" >> "${initdir}/etc/cmdline.d/90dmraid.conf" + fi + + inst_multiple dmraid + inst_multiple -o kpartx + inst "$(command -v partx)" /sbin/partx + + inst "$moddir/dmraid.sh" /sbin/dmraid_scan + + inst_rules 66-kpartx.rules 67-kpartx-compat.rules + + inst_libdir_file "libdmraid-events*.so*" + + inst_rules "$moddir/61-dmraid-imsm.rules" + #inst "$moddir/dmraid-cleanup.sh" /sbin/dmraid-cleanup + inst_hook pre-trigger 30 "$moddir/parse-dm.sh" +} diff --git a/modules.d/90dmraid/parse-dm.sh b/modules.d/90dmraid/parse-dm.sh new file mode 100755 index 0000000..d7a6b69 --- /dev/null +++ b/modules.d/90dmraid/parse-dm.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# nodmraid for anaconda / rc.sysinit compatibility +if ! getargbool 1 rd.dm -d -n rd_NO_DM || getarg "rd.dm=0" -d nodmraid; then + info "rd.dm=0: removing DM RAID activation" + udevproperty rd_NO_DM=1 +fi + +if ! command -v mdadm > /dev/null \ + || ! getargbool 1 rd.md.imsm -d -n rd_NO_MDIMSM -n noiswmd \ + || ! getargbool 1 rd.md -d -n rd_NO_MD; then + info "rd.md.imsm=0: no MD RAID for imsm/isw raids" + udevproperty rd_NO_MDIMSM=1 +fi + +if ! command -v mdadm > /dev/null \ + || ! getargbool 1 rd.md.ddf -n rd_NO_MDDDF -n noddfmd \ + || ! getargbool 1 rd.md -d -n rd_NO_MD; then + info "rd.md.ddf=0: no MD RAID for SNIA ddf raids" + udevproperty rd_NO_MDDDF=1 +fi + +DM_RAIDS=$(getargs rd.dm.uuid -d rd_DM_UUID=) + +if [ -z "$DM_RAIDS" ] && ! getargbool 0 rd.auto; then + udevproperty rd_NO_DM=1 +fi diff --git a/modules.d/90dmsquash-live-autooverlay/create-overlay-genrules.sh b/modules.d/90dmsquash-live-autooverlay/create-overlay-genrules.sh new file mode 100755 index 0000000..8372b50 --- /dev/null +++ b/modules.d/90dmsquash-live-autooverlay/create-overlay-genrules.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +case "$root" in + live:/dev/*) + printf 'SYMLINK=="%s", RUN+="/sbin/initqueue --settled --onetime --unique /sbin/create-overlay %s"\n' \ + "${root#live:/dev/}" "${root#live:}" >> /etc/udev/rules.d/95-create-overlay.rules + wait_for_dev -n "${root#live:}" + ;; +esac diff --git a/modules.d/90dmsquash-live-autooverlay/create-overlay.sh b/modules.d/90dmsquash-live-autooverlay/create-overlay.sh new file mode 100755 index 0000000..10e8ea5 --- /dev/null +++ b/modules.d/90dmsquash-live-autooverlay/create-overlay.sh @@ -0,0 +1,119 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if getargbool 0 rd.live.debug -n -y rdlivedebug; then + exec > /tmp/create-overlay.$$.out + exec 2>> /tmp/create-overlay.$$.out + set -x +fi + +gatherData() { + overlay=$(getarg rd.live.overlay) + if [ -z "$overlay" ]; then + info "Skipping overlay creation: kernel command line parameter 'rd.live.overlay' is not set" + exit 0 + fi + # shellcheck disable=SC2086 + if ! str_starts ${overlay} LABEL=; then + die "Overlay creation failed: the partition must be set by LABEL in the 'rd.live.overlay' kernel parameter" + fi + + overlayLabel=${overlay#LABEL=} + # shellcheck disable=SC2086 + if [ -b /dev/disk/by-label/${overlayLabel} ]; then + info "Skipping overlay creation: overlay already exists" + exit 0 + fi + + filesystem=$(getarg rd.live.overlay.cowfs) + [ -z "$filesystem" ] && filesystem="ext4" + if [ "$filesystem" != "ext4" ] && [ "$filesystem" != "xfs" ] && [ "$filesystem" != "btrfs" ]; then + die "Overlay creation failed: only ext4, xfs, and btrfs are supported in the 'rd.live.overlay.cowfs' kernel parameter" + fi + + live_dir=$(getarg rd.live.dir) + [ -z "$live_dir" ] && live_dir="LiveOS" + + [ -z "$1" ] && exit 1 + rootDevice=$1 + + # The kernel command line's 'root=' parameter was parsed into the $root variable by the dmsquash-live module. + # $root contains the path to a symlink within /dev/disk/by-label, which points to a partition. + # This script needs that partition's parent block device. + # shellcheck disable=SC2046 + # shellcheck disable=SC2086 + rootDeviceAbsolutePath=$(readlink -f ${rootDevice}) + rootDeviceSysfsPath=/sys/class/block/${rootDeviceAbsolutePath##*/} + if [ -f "${rootDeviceSysfsPath}/partition" ]; then + # shellcheck disable=SC2086 + read -r partition < ${rootDeviceSysfsPath}/partition + else + partition=0 + fi + # shellcheck disable=SC2086 + read -r readonly < ${rootDeviceSysfsPath}/ro + # shellcheck disable=SC2086 + if [ "$partition" != "1" ] || [ "$readonly" != "0" ]; then + info "Skipping overlay creation: unpartitioned or read-only media detected" + exit 0 + fi + # shellcheck disable=SC2046 + # shellcheck disable=SC2086 + fullDriveSysfsPath=$(readlink -f ${rootDeviceSysfsPath}/..) + blockDevice=/dev/${fullDriveSysfsPath##*/} + currentPartitionCount=$(grep --count -E "${blockDevice#/dev/}[0-9]+" /proc/partitions) + + # shellcheck disable=SC2086 + freeSpaceStart=$(parted --script ${blockDevice} unit % print free \ + | awk -v x=${currentPartitionCount} '$1 == x {getline; print $1}') + if [ -z "$freeSpaceStart" ]; then + info "Skipping overlay creation: there is no free space after the last partition" + exit 0 + fi + partitionStart=$((${freeSpaceStart%.*} + 1)) + if [ $partitionStart -eq 100 ]; then + info "Skipping overlay creation: there is not enough free space after the last partition" + exit 0 + fi + + overlayPartition=${blockDevice}$((currentPartitionCount + 1)) + + label=$(blkid --match-tag LABEL --output value "$rootDevice") + uuid=$(blkid --match-tag UUID --output value "$rootDevice") + if [ -z "$label" ] || [ -z "$uuid" ]; then + die "Overlay creation failed: failed to look up root device label and UUID" + fi +} + +createPartition() { + # shellcheck disable=SC2086 + parted --script --align optimal ${blockDevice} mkpart primary ${partitionStart}% 100% +} + +createFilesystem() { + # shellcheck disable=SC2086 + mkfs.${filesystem} -L ${overlayLabel} ${overlayPartition} + + baseDir=/run/initramfs/create-overlayfs + mkdir -p ${baseDir} + # shellcheck disable=SC2086 + mount -t ${filesystem} ${overlayPartition} ${baseDir} + + mkdir -p ${baseDir}/${live_dir}/ovlwork + # shellcheck disable=SC2086 + mkdir ${baseDir}/${live_dir}/overlay-${label}-${uuid} + + umount ${baseDir} + rm -r ${baseDir} +} + +main() { + gatherData "$1" + createPartition + udevsettle + createFilesystem + udevsettle +} + +main "$1" diff --git a/modules.d/90dmsquash-live-autooverlay/module-setup.sh b/modules.d/90dmsquash-live-autooverlay/module-setup.sh new file mode 100755 index 0000000..a97ca5a --- /dev/null +++ b/modules.d/90dmsquash-live-autooverlay/module-setup.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +check() { + # including a module dedicated to live environments in a host-only initrd doesn't make sense + [[ $hostonly ]] && return 1 + return 255 +} + +depends() { + echo dmsquash-live + return 0 +} + +installkernel() { + instmods btrfs ext4 xfs +} + +install() { + inst_multiple awk blkid cat grep mkdir mount parted readlink rmdir tr umount + inst_multiple -o mkfs.btrfs mkfs.ext4 mkfs.xfs + inst_hook pre-udev 25 "$moddir/create-overlay-genrules.sh" + inst_script "$moddir/create-overlay.sh" "/sbin/create-overlay" + dracut_need_initqueue +} diff --git a/modules.d/90dmsquash-live-ntfs/module-setup.sh b/modules.d/90dmsquash-live-ntfs/module-setup.sh new file mode 100755 index 0000000..7aa5802 --- /dev/null +++ b/modules.d/90dmsquash-live-ntfs/module-setup.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +check() { + require_binaries ntfs-3g || return 1 + return 255 +} + +depends() { + echo dmsquash-live + return 0 +} + +install() { + inst_multiple fusermount mount.fuse ntfs-3g + inst_script "$moddir/mount-ntfs-3g.sh" "/sbin/mount-ntfs-3g" + dracut_need_initqueue +} + +installkernel() { + hostonly='' instmods fuse +} diff --git a/modules.d/90dmsquash-live-ntfs/mount-ntfs-3g.sh b/modules.d/90dmsquash-live-ntfs/mount-ntfs-3g.sh new file mode 100755 index 0000000..289205c --- /dev/null +++ b/modules.d/90dmsquash-live-ntfs/mount-ntfs-3g.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +type vwarn > /dev/null 2>&1 || . /lib/dracut-lib.sh + +# Symlinking /usr/bin/ntfs-3g as /sbin/mount.ntfs seems to boot +# at the first glance, but ends with lots and lots of squashfs +# errors, because systemd attempts to kill the ntfs-3g process?! +# See https://systemd.io/ROOT_STORAGE_DAEMONS/ +if [ -x "/usr/bin/ntfs-3g" ]; then + ( + ln -s /usr/bin/ntfs-3g /run/@ntfs-3g + (sleep 1 && rm /run/@ntfs-3g) & + # shellcheck disable=SC2123 + PATH=/run + exec @ntfs-3g "$@" + ) | vwarn +else + die "Failed to mount block device of live image: Missing NTFS support" + exit 1 +fi diff --git a/modules.d/90dmsquash-live/apply-live-updates.sh b/modules.d/90dmsquash-live/apply-live-updates.sh new file mode 100755 index 0000000..a5a5a39 --- /dev/null +++ b/modules.d/90dmsquash-live/apply-live-updates.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +if [ -h /dev/root ] && [ -d /run/initramfs/live/updates -o -d /updates ]; then + info "Applying updates to live image..." + mount -o bind /run "$NEWROOT"/run + # avoid overwriting symlinks (e.g. /lib -> /usr/lib) with directories + for d in /updates /run/initramfs/live/updates; do + [ -d "$d" ] || continue + ( + cd "$d" || return 0 + find . -depth -type d -exec mkdir -p "$NEWROOT/{}" \; + find . -depth \! -type d -exec cp -a "{}" "$NEWROOT/{}" \; + ) + done + umount "$NEWROOT"/run +fi +# release resources on iso-scan boots with rd.live.ram +if [ -d /run/initramfs/isoscan ] \ + && [ -f /run/initramfs/squashed.img -o -f /run/initramfs/rootfs.img ]; then + umount --detach-loop /run/initramfs/live + umount /run/initramfs/isoscan +fi diff --git a/modules.d/90dmsquash-live/checkisomd5@.service b/modules.d/90dmsquash-live/checkisomd5@.service new file mode 100644 index 0000000..c4ca10f --- /dev/null +++ b/modules.d/90dmsquash-live/checkisomd5@.service @@ -0,0 +1,14 @@ +[Unit] +Description=Media check on %f +DefaultDependencies=no +Before=shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=no +ExecStart=/bin/checkisomd5 --verbose %f +StandardInput=tty-force +StandardOutput=inherit +StandardError=inherit +TimeoutSec=0 +SuccessExitStatus=2 diff --git a/modules.d/90dmsquash-live/dmsquash-generator.sh b/modules.d/90dmsquash-live/dmsquash-generator.sh new file mode 100755 index 0000000..8e3dfe8 --- /dev/null +++ b/modules.d/90dmsquash-live/dmsquash-generator.sh @@ -0,0 +1,80 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +[ -z "$root" ] && root=$(getarg root=) + +# support legacy syntax of passing liveimg and then just the base root +if getargbool 0 rd.live.image -d -y liveimg; then + liveroot="live:$root" +fi + +if [ "${root%%:*}" = "live" ]; then + liveroot=$root +fi + +[ "${liveroot%%:*}" = "live" ] || exit 0 + +case "$liveroot" in + live:LABEL=* | LABEL=* | live:UUID=* | UUID=* | live:PARTUUID=* | PARTUUID=* | live:PARTLABEL=* | PARTLABEL=*) + root="live:$(label_uuid_to_dev "${root#live:}")" + rootok=1 + ;; + live:CDLABEL=* | CDLABEL=*) + root="${root#live:}" + root="$(echo "$root" | sed 's,/,\\x2f,g;s, ,\\x20,g')" + root="live:/dev/disk/by-label/${root#CDLABEL=}" + rootok=1 + ;; + live:/*.[Ii][Ss][Oo] | /*.[Ii][Ss][Oo]) + root="${root#live:}" + root="liveiso:${root}" + rootok=1 + ;; + live:/dev/*) + rootok=1 + ;; + live:/*.[Ii][Mm][Gg] | /*.[Ii][Mm][Gg]) + [ -f "${root#live:}" ] && rootok=1 + ;; +esac + +[ "$rootok" != "1" ] && exit 0 + +GENERATOR_DIR="$2" +[ -z "$GENERATOR_DIR" ] && exit 1 +[ -d "$GENERATOR_DIR" ] || mkdir -p "$GENERATOR_DIR" + +getargbool 0 rd.live.overlay.readonly -d -y readonly_overlay && readonly_overlay="--readonly" || readonly_overlay="" +getargbool 0 rd.live.overlay.overlayfs && overlayfs="yes" +[ -e /xor_overlayfs ] && xor_overlayfs="yes" +[ -e /xor_readonly ] && xor_readonly="--readonly" +ROOTFLAGS="$(getarg rootflags)" +{ + echo "[Unit]" + echo "Before=initrd-root-fs.target" + echo "[Mount]" + echo "Where=/sysroot" + if [ "$overlayfs$xor_overlayfs" = "yes" ]; then + echo "What=LiveOS_rootfs" + if [ "$readonly_overlay$xor_readonly" = "--readonly" ]; then + ovlfs=lowerdir=/run/overlayfs-r:/run/rootfsbase + else + ovlfs=lowerdir=/run/rootfsbase + fi + echo "Options=${ROOTFLAGS},${ovlfs},upperdir=/run/overlayfs,workdir=/run/ovlwork" + echo "Type=overlay" + _dev=LiveOS_rootfs + else + echo "What=/dev/mapper/live-rw" + [ -n "$ROOTFLAGS" ] && echo "Options=${ROOTFLAGS}" + _dev='dev-mapper-live\x2drw' + fi +} > "$GENERATOR_DIR"/sysroot.mount + +mkdir -p "$GENERATOR_DIR/$_dev.device.d" +{ + echo "[Unit]" + echo "JobTimeoutSec=3000" + echo "JobRunningTimeoutSec=3000" +} > "$GENERATOR_DIR/$_dev.device.d/timeout.conf" diff --git a/modules.d/90dmsquash-live/dmsquash-live-genrules.sh b/modules.d/90dmsquash-live/dmsquash-live-genrules.sh new file mode 100755 index 0000000..8c7cad8 --- /dev/null +++ b/modules.d/90dmsquash-live/dmsquash-live-genrules.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +case "$root" in + live:/dev/*) + { + printf 'KERNEL=="%s", RUN+="/sbin/initqueue --settled --onetime --unique /sbin/dmsquash-live-root %s"\n' \ + "${root#live:/dev/}" "${root#live:}" + printf 'SYMLINK=="%s", RUN+="/sbin/initqueue --settled --onetime --unique /sbin/dmsquash-live-root %s"\n' \ + "${root#live:/dev/}" "${root#live:}" + } >> /etc/udev/rules.d/99-live-squash.rules + wait_for_dev -n "${root#live:}" + ;; + live:*) + if [ -f "${root#live:}" ]; then + /sbin/initqueue --settled --onetime --unique /sbin/dmsquash-live-root "${root#live:}" + fi + ;; +esac diff --git a/modules.d/90dmsquash-live/dmsquash-live-root.sh b/modules.d/90dmsquash-live/dmsquash-live-root.sh new file mode 100755 index 0000000..e808339 --- /dev/null +++ b/modules.d/90dmsquash-live/dmsquash-live-root.sh @@ -0,0 +1,432 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +type det_fs > /dev/null 2>&1 || . /lib/fs-lib.sh + +command -v unpack_archive > /dev/null || . /lib/img-lib.sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +if getargbool 0 rd.live.debug -n -y rdlivedebug; then + exec > /tmp/liveroot.$$.out + exec 2>> /tmp/liveroot.$$.out + set -x +fi + +[ -z "$1" ] && exit 1 +livedev="$1" + +# parse various live image specific options that make sense to be +# specified as their own things +live_dir=$(getarg rd.live.dir -d live_dir) +[ -z "$live_dir" ] && live_dir="LiveOS" +squash_image=$(getarg rd.live.squashimg) +[ -z "$squash_image" ] && squash_image="squashfs.img" + +getargbool 0 rd.live.ram -d -y live_ram && live_ram="yes" +getargbool 0 rd.live.overlay.reset -d -y reset_overlay && reset_overlay="yes" +getargbool 0 rd.live.overlay.readonly -d -y readonly_overlay && readonly_overlay="--readonly" || readonly_overlay="" +overlay=$(getarg rd.live.overlay -d overlay) +getargbool 0 rd.writable.fsimg -d -y writable_fsimg && writable_fsimg="yes" +overlay_size=$(getarg rd.live.overlay.size=) +[ -z "$overlay_size" ] && overlay_size=32768 + +getargbool 0 rd.live.overlay.thin && thin_snapshot="yes" +getargbool 0 rd.live.overlay.overlayfs && overlayfs="yes" + +# Take a path to a disk label and return the parent disk if it is a partition +# Otherwise returns the original path +get_check_dev() { + local _udevinfo + dev_path="$(udevadm info -q path --name "$1")" + _udevinfo="$(udevadm info -q property --path "${dev_path}")" + strstr "$_udevinfo" "DEVTYPE=partition" || { + echo "$1" + return + } + parent="${dev_path%/*}" + _udevinfo="$(udevadm info -q property --path "${parent}")" + strstr "$_udevinfo" "DEVTYPE=disk" || { + echo "$1" + return + } + strstr "$_udevinfo" "ID_FS_TYPE=iso9660" || { + echo "$1" + return + } + + # Return the name of the parent disk device + echo "$_udevinfo" | grep "DEVNAME=" | sed 's/DEVNAME=//' +} + +# Find the right device to run check on +check_dev=$(get_check_dev "$livedev") +# CD/DVD media check +[ -b "$check_dev" ] && fs=$(det_fs "$check_dev") +if [ "$fs" = "iso9660" -o "$fs" = "udf" ]; then + check="yes" +fi +getarg rd.live.check -d check || check="" +if [ -n "$check" ]; then + type plymouth > /dev/null 2>&1 && plymouth --hide-splash + if [ -n "$DRACUT_SYSTEMD" ]; then + p=$(dev_unit_name "$check_dev") + systemctl start checkisomd5@"${p}".service + else + checkisomd5 --verbose "$check_dev" + fi + if [ $? -eq 1 ]; then + die "CD check failed!" + exit 1 + fi + type plymouth > /dev/null 2>&1 && plymouth --show-splash +fi + +ln -s "$livedev" /run/initramfs/livedev + +# determine filesystem type for a filesystem image +det_img_fs() { + udevadm settle >&2 + blkid -s TYPE -u noraid -o value "$1" +} + +load_fstype squashfs +CMDLINE=$(getcmdline) +for arg in $CMDLINE; do + case $arg in + ro | rw) liverw=$arg ;; + esac +done + +# mount the backing of the live image first +mkdir -m 0755 -p /run/initramfs/live +if [ -f "$livedev" ]; then + # no mount needed - we've already got the LiveOS image in initramfs + # check filesystem type and handle accordingly + fstype=$(det_img_fs "$livedev") + case $fstype in + squashfs) SQUASHED=$livedev ;; + auto) die "cannot mount live image (unknown filesystem type)" ;; + *) FSIMG=$livedev ;; + esac + [ -e /sys/fs/"$fstype" ] || modprobe "$fstype" +else + livedev_fstype=$(det_fs "$livedev") + if [ "$livedev_fstype" = "squashfs" ]; then + # no mount needed - we've already got the LiveOS image in $livedev + SQUASHED=$livedev + elif [ "$livedev_fstype" != "ntfs" ]; then + if ! mount -n -t "$livedev_fstype" -o "${liverw:-ro}" "$livedev" /run/initramfs/live; then + die "Failed to mount block device of live image" + exit 1 + fi + else + [ -x "/sbin/mount-ntfs-3g" ] && /sbin/mount-ntfs-3g -o "${liverw:-ro}" "$livedev" /run/initramfs/live + fi +fi + +# overlay setup helper function +do_live_overlay() { + # create a sparse file for the overlay + # overlay: if non-ram overlay searching is desired, do it, + # otherwise, create traditional overlay in ram + + l=$(blkid -s LABEL -o value "$livedev") || l="" + u=$(blkid -s UUID -o value "$livedev") || u="" + + if [ -z "$overlay" ]; then + pathspec="/${live_dir}/overlay-$l-$u" + elif strstr "$overlay" ":"; then + # pathspec specified, extract + pathspec=${overlay##*:} + fi + + if [ -z "$pathspec" -o "$pathspec" = "auto" ]; then + pathspec="/${live_dir}/overlay-$l-$u" + fi + devspec=${overlay%%:*} + + # need to know where to look for the overlay + if [ -z "$setup" -a -n "$devspec" -a -n "$pathspec" -a -n "$overlay" ]; then + mkdir -m 0755 -p /run/initramfs/overlayfs + if ismounted "$devspec"; then + devmnt=$(findmnt -e -v -n -o 'TARGET' --source "$devspec") + # We need $devspec writable for overlay storage + mount -o remount,rw "$devspec" + mount --bind "$devmnt" /run/initramfs/overlayfs + else + mount -n -t auto "$devspec" /run/initramfs/overlayfs || : + fi + if [ -f /run/initramfs/overlayfs$pathspec -a -w /run/initramfs/overlayfs$pathspec ]; then + OVERLAY_LOOPDEV=$(losetup -f --show ${readonly_overlay:+-r} /run/initramfs/overlayfs$pathspec) + over=$OVERLAY_LOOPDEV + umount -l /run/initramfs/overlayfs || : + oltype=$(det_img_fs "$OVERLAY_LOOPDEV") + if [ -z "$oltype" ] || [ "$oltype" = DM_snapshot_cow ]; then + if [ -n "$reset_overlay" ]; then + info "Resetting the Device-mapper overlay." + dd if=/dev/zero of="$OVERLAY_LOOPDEV" bs=64k count=1 conv=fsync 2> /dev/null + fi + if [ -n "$overlayfs" ]; then + unset -v overlayfs + [ -n "$DRACUT_SYSTEMD" ] && reloadsysrootmountunit=":>/xor_overlayfs;" + fi + setup="yes" + else + mount -n -t "$oltype" ${readonly_overlay:+-r} "$OVERLAY_LOOPDEV" /run/initramfs/overlayfs + if [ -d /run/initramfs/overlayfs/overlayfs ] \ + && [ -d /run/initramfs/overlayfs/ovlwork ]; then + ln -s /run/initramfs/overlayfs/overlayfs /run/overlayfs${readonly_overlay:+-r} + ln -s /run/initramfs/overlayfs/ovlwork /run/ovlwork${readonly_overlay:+-r} + if [ -z "$overlayfs" ] && [ -n "$DRACUT_SYSTEMD" ]; then + reloadsysrootmountunit=":>/xor_overlayfs;" + fi + overlayfs="required" + setup="yes" + fi + fi + elif [ -d /run/initramfs/overlayfs$pathspec ] \ + && [ -d /run/initramfs/overlayfs$pathspec/../ovlwork ]; then + ln -s /run/initramfs/overlayfs$pathspec /run/overlayfs${readonly_overlay:+-r} + ln -s /run/initramfs/overlayfs$pathspec/../ovlwork /run/ovlwork${readonly_overlay:+-r} + if [ -z "$overlayfs" ] && [ -n "$DRACUT_SYSTEMD" ]; then + reloadsysrootmountunit=":>/xor_overlayfs;" + fi + overlayfs="required" + setup="yes" + fi + fi + if [ -n "$overlayfs" ]; then + if ! load_fstype overlay; then + if [ "$overlayfs" = required ]; then + die "OverlayFS is required but not available." + exit 1 + fi + [ -n "$DRACUT_SYSTEMD" ] && reloadsysrootmountunit=":>/xor_overlayfs;" + m='OverlayFS is not available; using temporary Device-mapper overlay.' + info "$m" + unset -v overlayfs setup + fi + fi + + if [ -z "$setup" -o -n "$readonly_overlay" ]; then + if [ -n "$setup" ]; then + warn "Using temporary overlay." + elif [ -n "$devspec" -a -n "$pathspec" ]; then + [ -z "$m" ] \ + && m=' Unable to find a persistent overlay; using a temporary one.' + m="$m"' + All root filesystem changes will be lost on shutdown. + Press [Enter] to continue.' + printf "\n\n\n\n%s\n\n\n" "${m}" > /dev/kmsg + if [ -n "$DRACUT_SYSTEMD" ]; then + if type plymouth > /dev/null 2>&1 && plymouth --ping; then + if getargbool 0 rhgb || getargbool 0 splash; then + m='>>> +>>> +>>> + + +'"$m" + m="${m%n.*}"'n. + + +<<< +<<< +<<<' + plymouth display-message --text="${m}" + else + plymouth ask-question --prompt="${m}" --command=true + fi + else + m=">>>$(printf '%s' "$m" | tr -d '\n') <<<" + systemd-ask-password --timeout=0 "${m}" + fi + else + type plymouth > /dev/null 2>&1 && plymouth --ping && plymouth --quit + printf '\n\n%s' "$m" + read -r _ + fi + fi + if [ -n "$overlayfs" ]; then + if [ -n "$readonly_overlay" ] && ! [ -h /run/overlayfs-r ]; then + info "No persistent overlay found." + unset -v readonly_overlay + [ -n "$DRACUT_SYSTEMD" ] && reloadsysrootmountunit="${reloadsysrootmountunit}:>/xor_readonly;" + fi + else + dd if=/dev/null of=/overlay bs=1024 count=1 seek=$((overlay_size * 1024)) 2> /dev/null + if [ -n "$setup" -a -n "$readonly_overlay" ]; then + RO_OVERLAY_LOOPDEV=$(losetup -f --show /overlay) + over=$RO_OVERLAY_LOOPDEV + else + OVERLAY_LOOPDEV=$(losetup -f --show /overlay) + over=$OVERLAY_LOOPDEV + fi + fi + fi + + # set up the snapshot + if [ -z "$overlayfs" ]; then + if [ -n "$readonly_overlay" ] && [ -n "$OVERLAY_LOOPDEV" ]; then + echo 0 "$sz" snapshot "$BASE_LOOPDEV" "$OVERLAY_LOOPDEV" P 8 | dmsetup create --readonly live-ro + base="/dev/mapper/live-ro" + else + base=$BASE_LOOPDEV + fi + fi + + if [ -n "$thin_snapshot" ]; then + modprobe dm_thin_pool + mkdir -m 0755 -p /run/initramfs/thin-overlay + + # In block units (512b) + thin_data_sz=$((overlay_size * 1024 * 1024 / 512)) + thin_meta_sz=$((thin_data_sz / 10)) + + # It is important to have the backing file on a tmpfs + # this is needed to let the loopdevice support TRIM + dd if=/dev/null of=/run/initramfs/thin-overlay/meta bs=1b count=1 seek=$((thin_meta_sz)) 2> /dev/null + dd if=/dev/null of=/run/initramfs/thin-overlay/data bs=1b count=1 seek=$((thin_data_sz)) 2> /dev/null + + THIN_META_LOOPDEV=$(losetup --show -f /run/initramfs/thin-overlay/meta) + THIN_DATA_LOOPDEV=$(losetup --show -f /run/initramfs/thin-overlay/data) + + echo 0 $thin_data_sz thin-pool "$THIN_META_LOOPDEV" "$THIN_DATA_LOOPDEV" 1024 1024 | dmsetup create live-overlay-pool + dmsetup message /dev/mapper/live-overlay-pool 0 "create_thin 0" + + # Create a snapshot of the base image + echo 0 "$sz" thin /dev/mapper/live-overlay-pool 0 "$base" | dmsetup create live-rw + elif [ -z "$overlayfs" ]; then + echo 0 "$sz" snapshot "$base" "$over" PO 8 | dmsetup create live-rw + fi + + # Create a device for the ro base of overlaid file systems. + if [ -z "$overlayfs" ]; then + echo 0 "$sz" linear "$BASE_LOOPDEV" 0 | dmsetup create --readonly live-base + fi + ln -s "$BASE_LOOPDEV" /dev/live-base +} +# end do_live_overlay() + +# we might have an embedded fs image on squashfs (compressed live) +if [ -e /run/initramfs/live/${live_dir}/${squash_image} ]; then + SQUASHED="/run/initramfs/live/${live_dir}/${squash_image}" +fi +if [ -e "$SQUASHED" ]; then + if [ -n "$live_ram" ]; then + imgsize=$(($(stat -c %s -- $SQUASHED) / (1024 * 1024))) + check_live_ram $imgsize + echo 'Copying live image to RAM...' > /dev/kmsg + echo ' (this may take a minute)' > /dev/kmsg + dd if=$SQUASHED of=/run/initramfs/squashed.img bs=512 2> /dev/null + echo 'Done copying live image to RAM.' > /dev/kmsg + SQUASHED="/run/initramfs/squashed.img" + fi + + SQUASHED_LOOPDEV=$(losetup -f) + losetup -r "$SQUASHED_LOOPDEV" $SQUASHED + mkdir -m 0755 -p /run/initramfs/squashfs + mount -n -t squashfs -o ro "$SQUASHED_LOOPDEV" /run/initramfs/squashfs + + if [ -d /run/initramfs/squashfs/LiveOS ]; then + if [ -f /run/initramfs/squashfs/LiveOS/rootfs.img ]; then + FSIMG="/run/initramfs/squashfs/LiveOS/rootfs.img" + elif [ -f /run/initramfs/squashfs/LiveOS/ext3fs.img ]; then + FSIMG="/run/initramfs/squashfs/LiveOS/ext3fs.img" + fi + elif [ -d /run/initramfs/squashfs/proc ]; then + FSIMG=$SQUASHED + if [ -z "$overlayfs" ] && [ -n "$DRACUT_SYSTEMD" ]; then + reloadsysrootmountunit=":>/xor_overlayfs;" + fi + overlayfs="required" + else + die "Failed to find a root filesystem in $SQUASHED." + exit 1 + fi +else + # we might have an embedded fs image to use as rootfs (uncompressed live) + if [ -e /run/initramfs/live/${live_dir}/rootfs.img ]; then + FSIMG="/run/initramfs/live/${live_dir}/rootfs.img" + elif [ -e /run/initramfs/live/${live_dir}/ext3fs.img ]; then + FSIMG="/run/initramfs/live/${live_dir}/ext3fs.img" + fi + if [ -n "$live_ram" ]; then + echo 'Copying live image to RAM...' > /dev/kmsg + echo ' (this may take a minute or so)' > /dev/kmsg + dd if=$FSIMG of=/run/initramfs/rootfs.img bs=512 2> /dev/null + echo 'Done copying live image to RAM.' > /dev/kmsg + FSIMG='/run/initramfs/rootfs.img' + fi +fi + +if [ -n "$FSIMG" ]; then + if [ -n "$writable_fsimg" ]; then + # mount the provided filesystem read/write + echo "Unpacking live filesystem (may take some time)" > /dev/kmsg + mkdir -m 0755 -p /run/initramfs/fsimg/ + if [ -n "$SQUASHED" ]; then + cp -v $FSIMG /run/initramfs/fsimg/rootfs.img + else + unpack_archive $FSIMG /run/initramfs/fsimg/ + fi + FSIMG=/run/initramfs/fsimg/rootfs.img + fi + # For writable DM images... + readonly_base=1 + if [ -z "$SQUASHED" -a -n "$live_ram" -a -z "$overlayfs" ] \ + || [ -n "$writable_fsimg" ] \ + || [ "$overlay" = none -o "$overlay" = None -o "$overlay" = NONE ]; then + if [ -z "$readonly_overlay" ]; then + unset readonly_base + setup=rw + else + setup=yes + fi + fi + if [ "$FSIMG" = "$SQUASHED" ]; then + BASE_LOOPDEV=$SQUASHED_LOOPDEV + else + BASE_LOOPDEV=$(losetup -f --show ${readonly_base:+-r} $FSIMG) + sz=$(blockdev --getsz "$BASE_LOOPDEV") + fi + if [ "$setup" = rw ]; then + echo 0 "$sz" linear "$BASE_LOOPDEV" 0 | dmsetup create live-rw + else + # Add a DM snapshot or OverlayFS for writes. + do_live_overlay + fi +fi + +if [ -n "$reloadsysrootmountunit" ]; then + eval "$reloadsysrootmountunit" + systemctl daemon-reload +fi + +ROOTFLAGS="$(getarg rootflags)" + +if [ "$overlayfs" = required ]; then + echo "rd.live.overlay.overlayfs=1" > /etc/cmdline.d/dmsquash-need-overlay.conf +fi + +if [ -n "$overlayfs" ]; then + if [ -n "$FSIMG" ]; then + mkdir -m 0755 -p /run/rootfsbase + mount -r $FSIMG /run/rootfsbase + else + ln -sf /run/initramfs/live /run/rootfsbase + fi +else + if [ -z "$DRACUT_SYSTEMD" ]; then + [ -n "$ROOTFLAGS" ] && ROOTFLAGS="-o $ROOTFLAGS" + printf 'mount %s /dev/mapper/live-rw %s\n' "$ROOTFLAGS" "$NEWROOT" > "$hookdir"/mount/01-$$-live.sh + fi +fi +[ -e "$SQUASHED" ] && umount -l /run/initramfs/squashfs + +ln -s null /dev/root + +need_shutdown + +exit 0 diff --git a/modules.d/90dmsquash-live/dmsquash-liveiso-genrules.sh b/modules.d/90dmsquash-live/dmsquash-liveiso-genrules.sh new file mode 100755 index 0000000..a5810f7 --- /dev/null +++ b/modules.d/90dmsquash-live/dmsquash-liveiso-genrules.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ "${root%%:*}" = "liveiso" ]; then + { + # shellcheck disable=SC2016 + printf 'KERNEL=="loop-control", RUN+="/sbin/initqueue --settled --onetime --unique /sbin/dmsquash-live-root `/sbin/losetup -f --show %s`"\n' \ + "${root#liveiso:}" + } >> /etc/udev/rules.d/99-liveiso-mount.rules +fi diff --git a/modules.d/90dmsquash-live/iso-scan.sh b/modules.d/90dmsquash-live/iso-scan.sh new file mode 100755 index 0000000..fa06b33 --- /dev/null +++ b/modules.d/90dmsquash-live/iso-scan.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +isofile=$1 + +[ -z "$isofile" ] && exit 1 + +ismounted "/run/initramfs/isoscan" && exit 0 + +mkdir -p "/run/initramfs/isoscan" + +do_iso_scan() { + local _name + local dev + for dev in /dev/disk/by-uuid/*; do + _name=$(dev_unit_name "$dev") + [ -e /tmp/isoscan-"${_name}" ] && continue + : > /tmp/isoscan-"${_name}" + mount -t auto -o ro "$dev" "/run/initramfs/isoscan" || continue + if [ -f "/run/initramfs/isoscan/$isofile" ]; then + losetup -f "/run/initramfs/isoscan/$isofile" + udevadm trigger --action=add > /dev/null 2>&1 + ln -s "$dev" /run/initramfs/isoscandev + rm -f -- "$job" + exit 0 + else + umount "/run/initramfs/isoscan" + fi + done +} + +do_iso_scan + +rmdir "/run/initramfs/isoscan" +exit 1 diff --git a/modules.d/90dmsquash-live/module-setup.sh b/modules.d/90dmsquash-live/module-setup.sh new file mode 100755 index 0000000..b905e3d --- /dev/null +++ b/modules.d/90dmsquash-live/module-setup.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# called by dracut +check() { + # a live host-only image doesn't really make a lot of sense + [[ $hostonly ]] && return 1 + return 255 +} + +# called by dracut +depends() { + # if dmsetup is not installed, then we cannot support fedora/red hat + # style live images + echo dm rootfs-block img-lib overlayfs + return 0 +} + +# called by dracut +installkernel() { + instmods squashfs loop iso9660 +} + +# called by dracut +install() { + inst_multiple umount dmsetup blkid dd losetup blockdev find rmdir grep + inst_multiple -o checkisomd5 + inst_hook cmdline 30 "$moddir/parse-dmsquash-live.sh" + inst_hook cmdline 31 "$moddir/parse-iso-scan.sh" + inst_hook pre-udev 30 "$moddir/dmsquash-live-genrules.sh" + inst_hook pre-udev 30 "$moddir/dmsquash-liveiso-genrules.sh" + inst_hook pre-pivot 20 "$moddir/apply-live-updates.sh" + inst_script "$moddir/dmsquash-live-root.sh" "/sbin/dmsquash-live-root" + inst_script "$moddir/iso-scan.sh" "/sbin/iso-scan" + if dracut_module_included "systemd-initrd"; then + inst_script "$moddir/dmsquash-generator.sh" "$systemdutildir"/system-generators/dracut-dmsquash-generator + inst_simple "$moddir/checkisomd5@.service" "/etc/systemd/system/checkisomd5@.service" + fi + dracut_need_initqueue +} diff --git a/modules.d/90dmsquash-live/parse-dmsquash-live.sh b/modules.d/90dmsquash-live/parse-dmsquash-live.sh new file mode 100755 index 0000000..de910b3 --- /dev/null +++ b/modules.d/90dmsquash-live/parse-dmsquash-live.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# live images are specified with +# root=live:backingdev + +[ -z "$root" ] && root=$(getarg root=) + +# support legacy syntax of passing liveimg and then just the base root +if getargbool 0 rd.live.image -d -y liveimg; then + liveroot="live:$root" +fi + +if [ "${root%%:*}" = "live" ]; then + liveroot=$root +fi + +[ "${liveroot%%:*}" = "live" ] || return 1 + +modprobe -q loop + +case "$liveroot" in + live:LABEL=* | LABEL=* | live:UUID=* | UUID=* | live:PARTUUID=* | PARTUUID=* | live:PARTLABEL=* | PARTLABEL=*) + root="live:$(label_uuid_to_dev "${root#live:}")" + rootok=1 + ;; + live:CDLABEL=* | CDLABEL=*) + root="${root#live:}" + root="$(echo "$root" | sed 's,/,\\x2f,g;s, ,\\x20,g')" + root="live:/dev/disk/by-label/${root#CDLABEL=}" + rootok=1 + ;; + live:/*.[Ii][Ss][Oo] | /*.[Ii][Ss][Oo]) + root="${root#live:}" + root="liveiso:${root}" + rootok=1 + ;; + live:/dev/*) + root="live:${root#live:}" + rootok=1 + ;; + live:/*.[Ii][Mm][Gg] | /*.[Ii][Mm][Gg]) + [ -f "${root#live:}" ] && rootok=1 + ;; + live:nfs*) + rootok=1 + ;; +esac + +[ "$rootok" = "1" ] || return 1 + +info "root was $liveroot, is now $root" + +# make sure that init doesn't complain +[ -z "$root" ] && root="live" + +wait_for_dev -n /dev/root + +return 0 diff --git a/modules.d/90dmsquash-live/parse-iso-scan.sh b/modules.d/90dmsquash-live/parse-iso-scan.sh new file mode 100755 index 0000000..1dd2d37 --- /dev/null +++ b/modules.d/90dmsquash-live/parse-iso-scan.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# live images are specified with +# root=live:backingdev + +isofile=$(getarg iso-scan/filename) + +if [ -n "$isofile" ]; then + /sbin/initqueue --settled --unique /sbin/iso-scan "$isofile" +fi diff --git a/modules.d/90kernel-modules-extra/module-setup.sh b/modules.d/90kernel-modules-extra/module-setup.sh new file mode 100755 index 0000000..85e2e0a --- /dev/null +++ b/modules.d/90kernel-modules-extra/module-setup.sh @@ -0,0 +1,186 @@ +#!/bin/bash + +# called by dracut +# +# Parses depmod configuration and calls instmods for out-of-tree kernel +# modules found. Specifically, kernel modules inside directories that +# come from the following places are included (if these kernel modules +# are present in modules.dep): +# - "search" configuration option; +# - "override" configuration option (matching an exact file name constructed +# by concatenating the provided directory and the kernel module name); +# - "external" configuration option (if "external" is a part of "search" +# configuration). +# (See depmod.d(5) for details.) +# +# This module has the following variables available for configuration: +# - "depmod_modules_dep" - Path to the modules.dep file +# ("$srcmods/modules.dep" by default); +# - "depmod_module_dir" - Directory containing kernel modules ("$srcmods" +# by default); +# - "depmod_configs" - array of depmod configuration paths to parse +# (as supplied to depmod -C, ("/run/depmod.d/" +# "/etc/depmod.d/" "/lib/depmod.d/") by default). +installkernel() { + : "${depmod_modules_dep:=$srcmods/modules.dep}" + : "${depmod_module_dir:=$srcmods}" + + [[ -f ${depmod_modules_dep} ]] || return 0 + + # Message printers with custom prefix + local mod_name="kernel-modules-extra" + prinfo() { dinfo " ${mod_name}: $*"; } + prdebug() { ddebug " ${mod_name}: $*"; } + + # Escape a string for usage as a part of extended regular expression. + # $1 - string to escape + re_escape() { + printf "%s" "$1" | sed 's/\([.+?^$\/\\|()\[]\|\]\)/\\\0/' + } + + local cfg + local cfgs=() + local search_list="" + local overrides=() + local external_dirs=() + local e f + + ## Gathering and sorting configuration file list + + [ -n "${depmod_configs[*]-}" ] \ + || depmod_configs=(/run/depmod.d /etc/depmod.d /lib/depmod.d) + + for cfg in "${depmod_configs[@]}"; do + [ -e "$cfg" ] || { + prdebug "configuration source \"$cfg\" does not exist" + continue + } + + # '/' is used as a separator between configuration name and + # configuration path + if [ -d "$cfg" ]; then + for f in "$cfg/"*.conf; do + [[ -e $f && ! -d $f ]] || { + prdebug "configuration source" \ + "\"$cfg\" is ignored" \ + "(directory or doesn't exist)" + continue + } + cfgs+=("${f##*/}/$f") + done + else + cfgs+=("${cfg##*/}/$cfg") + fi + done + + if ((${#cfgs[@]} > 0)); then + mapfile -t cfgs < <(printf '%s\n' "${cfgs[@]}" | LANG=C sort -u -k1,1 -t '/' | cut -f 2- -d '/') + fi + + ## Parse configurations + + for cfg in "${cfgs[@]}"; do + prdebug "parsing configuration file \"$cfg\"" + + local k v mod kverpat path + while read -r k v; do + case "$k" in + search) + search_list="$search_list $v" + prdebug "$cfg: added \"$v\" to the list of" \ + "search directories" + ;; + override) # module_name kver_pattern dir + read -r mod kverpat path <<< "$v" + + if [[ ! $mod || ! $kverpat || ! $path ]]; then + prinfo "$cfg: ignoring incorrect" \ + "override option: \"$k $v\"" + continue + fi + + if [[ '*' == "$kverpat" ]] \ + || [[ $kernel =~ $kverpat ]]; then + overrides+=("${path}/${mod}") + + prdebug "$cfg: added override" \ + "\"${path}/${mod}\"" + else + prdebug "$cfg: override \"$v\" is" \ + "ignored since \"$kverpat\"" \ + "doesn't match \"$kernel\"" + fi + ;; + external) # kverpat dir + read -r kverpat path <<< "$v" + + if [[ ! $kverpat || ! $path ]]; then + prinfo "$cfg: ignoring incorrect" \ + "external option: \"$k $v\"" + continue + fi + + if [[ '*' == "$kverpat" ]] \ + || [[ $kernel =~ $kverpat ]]; then + external_dirs+=("$path") + + prdebug "$cfg: added external" \ + "directory \"$path\"" + else + prdebug "$cfg: external directory" \ + "\"$path\" is ignored since" \ + "\"$kverpat\" doesn't match " \ + "\"$kernel\"" + fi + ;; + '#'* | '') # comments and empty strings + ;; + include | make_map_files) # ignored by depmod + ;; + *) + prinfo "$cfg: unknown depmod configuration" \ + "option \"$k $v\"" + ;; + esac + done < "$cfg" + done + + # "updates built-in" is the default search list + : "${search_list:=updates}" + + ## Build a list of regular expressions for grepping modules.dep + + local pathlist=() + for f in "${overrides[@]}"; do + pathlist+=("^$(re_escape "$f")") + done + + for f in $(printf "%s" "$search_list"); do + # Ignoring builtin modules + [[ $f == "built-in" ]] && continue + + if [[ $f == "external" ]]; then + for e in "${external_dirs[@]}"; do + pathlist+=("$(re_escape "${e%/}")/[^:]+") + done + fi + + pathlist+=("$(re_escape "${f%/}")/[^:]+") + done + + ## Filter modules.dep, canonicalise the resulting filenames and supply + ## them to instmods. + + ((${#pathlist[@]} > 0)) || return 0 + + printf "^%s\.ko(\.gz|\.bz2|\.xz|\.zst)?:\n" "${pathlist[@]}" \ + | (LANG=C grep -E -o -f - -- "$depmod_modules_dep" || exit 0) \ + | tr -d ':' \ + | ( + cd "$depmod_module_dir" || exit + xargs -r realpath -se -- + ) \ + | instmods || return 1 + + return 0 +} diff --git a/modules.d/90kernel-modules/insmodpost.sh b/modules.d/90kernel-modules/insmodpost.sh new file mode 100755 index 0000000..a7ab05b --- /dev/null +++ b/modules.d/90kernel-modules/insmodpost.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +. /lib/dracut-lib.sh + +for modlist in $(getargs rd.driver.post -d rdinsmodpost=); do + ( + IFS=, + for m in $modlist; do + modprobe "$m" + done + ) +done diff --git a/modules.d/90kernel-modules/module-setup.sh b/modules.d/90kernel-modules/module-setup.sh new file mode 100755 index 0000000..e217512 --- /dev/null +++ b/modules.d/90kernel-modules/module-setup.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +# called by dracut +installkernel() { + local _blockfuncs='ahci_platform_get_resources|ata_scsi_ioctl|scsi_add_host|blk_cleanup_queue|register_mtd_blktrans|scsi_esp_register|register_virtio_device|usb_stor_disconnect|mmc_add_host|sdhci_add_host|scsi_add_host_with_dma|blk_mq_alloc_disk|blk_mq_alloc_request|blk_mq_destroy_queue|blk_cleanup_disk' + local -A _hostonly_drvs + + record_block_dev_drv() { + + for _mod in $(get_dev_module /dev/block/"$1"); do + _hostonly_drvs["$_mod"]="$_mod" + done + + for _mod in $(get_blockdev_drv_through_sys "/sys/dev/block/$1"); do + _hostonly_drvs["$_mod"]="$_mod" + done + + ((${#_hostonly_drvs[@]} > 0)) && return 0 + return 1 + } + + install_block_modules_strict() { + hostonly='' instmods "${_hostonly_drvs[@]}" + } + + install_block_modules() { + instmods \ + scsi_dh_rdac scsi_dh_emc scsi_dh_alua \ + =drivers/usb/storage \ + =ide nvme vmd \ + virtio_blk virtio_scsi \ + =drivers/ufs + + dracut_instmods -o -s "${_blockfuncs}" "=drivers" + } + + if [[ -z $drivers ]]; then + hostonly='' instmods \ + hid_generic unix + + hostonly=$(optional_hostonly) instmods \ + ehci-hcd ehci-pci ehci-platform \ + ohci-hcd ohci-pci \ + uhci-hcd \ + usbhid \ + xhci-hcd xhci-pci xhci-plat-hcd \ + "=drivers/hid" \ + "=drivers/tty/serial" \ + "=drivers/input/serio" \ + "=drivers/input/keyboard" \ + "=drivers/pci/host" \ + "=drivers/pci/controller" \ + "=drivers/pinctrl" \ + "=drivers/usb/typec" \ + "=drivers/watchdog" + + instmods \ + yenta_socket spi_pxa2xx_platform \ + atkbd i8042 firewire-ohci pcmcia hv-vmbus \ + virtio virtio_ring virtio_pci pci_hyperv \ + "=drivers/pcmcia" + + if [[ ${DRACUT_ARCH:-$(uname -m)} == arm* || ${DRACUT_ARCH:-$(uname -m)} == aarch64 || ${DRACUT_ARCH:-$(uname -m)} == riscv* ]]; then + # arm/aarch64 specific modules + _blockfuncs+='|dw_mc_probe|dw_mci_pltfm_register|nvme_init_ctrl' + instmods \ + "=drivers/clk" \ + "=drivers/devfreq" \ + "=drivers/dma" \ + "=drivers/extcon" \ + "=drivers/gpio" \ + "=drivers/hwmon" \ + "=drivers/hwspinlock" \ + "=drivers/interconnect" \ + "=drivers/i2c/busses" \ + "=drivers/mailbox" \ + "=drivers/memory" \ + "=drivers/mfd" \ + "=drivers/mmc/core" \ + "=drivers/mmc/host" \ + "=drivers/nvmem" \ + "=drivers/phy" \ + "=drivers/power" \ + "=drivers/regulator" \ + "=drivers/reset" \ + "=drivers/rpmsg" \ + "=drivers/rtc" \ + "=drivers/soc" \ + "=drivers/spi" \ + "=drivers/usb/chipidea" \ + "=drivers/usb/dwc2" \ + "=drivers/usb/dwc3" \ + "=drivers/usb/host" \ + "=drivers/usb/isp1760" \ + "=drivers/usb/misc" \ + "=drivers/usb/musb" \ + "=drivers/usb/phy" \ + "=drivers/scsi/hisi_sas" + fi + + awk -F: '/^\// {print $1}' "$srcmods/modules.dep" 2> /dev/null | instmods + + # if not on hostonly mode, or there are hostonly block device + # install block drivers + if ! [[ $hostonly ]] \ + || for_each_host_dev_and_slaves_all record_block_dev_drv; then + hostonly='' instmods sg sr_mod sd_mod scsi_dh ata_piix + + if [[ $hostonly_mode == "strict" ]]; then + install_block_modules_strict + else + install_block_modules + fi + fi + + # if not on hostonly mode, install all known filesystems, + # if the required list is not set via the filesystems variable + if ! [[ $hostonly ]]; then + if [[ -z $filesystems ]]; then + dracut_instmods -o -P ".*/(kernel/fs/nfs|kernel/fs/nfsd|kernel/fs/lockd)/.*" '=fs' + fi + elif [[ "${host_fs_types[*]}" ]]; then + hostonly='' instmods "${host_fs_types[@]}" + fi + + arch=${DRACUT_ARCH:-$(uname -m)} + + # We don't want to play catch up with hash and encryption algorithms. + # To be safe, just use the hammer and include all crypto. + [[ $arch == x86_64 ]] && arch=x86 + [[ $arch == s390x ]] && arch=s390 + [[ $arch == aarch64 ]] && arch=arm64 + hostonly='' instmods "=crypto" + instmods "=arch/$arch/crypto" "=drivers/crypto" + fi + + inst_multiple -o "$depmodd/*.conf" + if [[ $hostonly ]]; then + inst_multiple -H -o "$depmodconfdir/*.conf" + fi + : +} + +# called by dracut +install() { + [[ -d /lib/modprobe.d ]] && inst_multiple -o "/lib/modprobe.d/*.conf" + [[ -d /usr/lib/modprobe.d ]] && inst_multiple -o "/usr/lib/modprobe.d/*.conf" + [[ $hostonly ]] && inst_multiple -H -o /etc/modprobe.d/*.conf /etc/modprobe.conf + if ! dracut_module_included "systemd"; then + inst_hook cmdline 01 "$moddir/parse-kernel.sh" + fi + inst_simple "$moddir/insmodpost.sh" /sbin/insmodpost.sh + inst_multiple -o sysctl +} diff --git a/modules.d/90kernel-modules/parse-kernel.sh b/modules.d/90kernel-modules/parse-kernel.sh new file mode 100755 index 0000000..d14f912 --- /dev/null +++ b/modules.d/90kernel-modules/parse-kernel.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +_modprobe_d=/etc/modprobe.d +if [ -d /usr/lib/modprobe.d ]; then + _modprobe_d=/usr/lib/modprobe.d +elif [ -d /lib/modprobe.d ]; then + _modprobe_d=/lib/modprobe.d +elif [ ! -d $_modprobe_d ]; then + mkdir -p $_modprobe_d +fi + +for i in $(getargs rd.driver.pre -d rdloaddriver=); do + ( + IFS=, + for p in $i; do + modprobe "$p" 2>&1 | vinfo + done + ) +done + +[ -d /etc/modprobe.d ] || mkdir -p /etc/modprobe.d + +for i in $(getargs rd.driver.blacklist -d rdblacklist=); do + ( + IFS=, + for p in $i; do + echo "blacklist $p" >> $_modprobe_d/initramfsblacklist.conf + done + ) +done + +for p in $(getargs rd.driver.post -d rdinsmodpost=); do + echo "blacklist $p" >> $_modprobe_d/initramfsblacklist.conf + _do_insmodpost=1 +done + +[ -n "$_do_insmodpost" ] && initqueue --settled --unique --onetime insmodpost.sh +unset _do_insmodpost _modprobe_d diff --git a/modules.d/90kernel-network-modules/module-setup.sh b/modules.d/90kernel-network-modules/module-setup.sh new file mode 100755 index 0000000..49823ef --- /dev/null +++ b/modules.d/90kernel-network-modules/module-setup.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + # Include wired net drivers, excluding wireless + local _arch=${DRACUT_ARCH:-$(uname -m)} + local _net_symbols='eth_type_trans|register_virtio_device|usbnet_open' + local _unwanted_drivers='/(wireless|isdn|uwb|net/ethernet|net/phy|net/team)/' + local _net_drivers + + if [[ $_arch == "s390" ]] || [[ $_arch == "s390x" ]]; then + dracut_instmods -o -P ".*${_unwanted_drivers}.*" -s "$_net_symbols" "=drivers/s390/net" + fi + + if [[ $hostonly_mode == 'strict' ]] && [[ -n ${hostonly_nics+x} ]]; then + for _nic in $hostonly_nics; do + mapfile -t _net_drivers < <(get_dev_module /sys/class/net/"$_nic") + if ((${#_net_drivers[@]} == 0)); then + derror "--hostonly-nics contains invalid NIC '$_nic'" + continue + fi + hostonly="" instmods "${_net_drivers[@]}" + done + return 0 + fi + + dracut_instmods -o -P ".*${_unwanted_drivers}.*" -s "$_net_symbols" "=drivers/net" + #instmods() will take care of hostonly + instmods \ + '=drivers/net/mdio' \ + '=drivers/net/phy' \ + '=drivers/net/team' \ + '=drivers/net/ethernet' \ + ecb arc4 bridge stp llc ipv6 bonding 8021q ipvlan macvlan af_packet virtio_net xennet +} + +# called by dracut +install() { + return 0 +} diff --git a/modules.d/90livenet/fetch-liveupdate.sh b/modules.d/90livenet/fetch-liveupdate.sh new file mode 100755 index 0000000..3ff2a9c --- /dev/null +++ b/modules.d/90livenet/fetch-liveupdate.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# fetch-liveupdate - fetch an update image for dmsquash-live media. +# this gets called by the "initqueue/online" hook for each network interface +# that comes online. + +# no updates requested? we're not needed. +[ -e /tmp/liveupdates.info ] || return 0 + +command -v getarg > /dev/null || . /lib/dracut-lib.sh +command -v fetch_url > /dev/null || . /lib/url-lib.sh +command -v unpack_img > /dev/null || . /lib/img-lib.sh + +read -r url < /tmp/liveupdates.info + +info "fetching live updates from $url" + +if ! fetch_url "$url" /tmp/updates.img; then + warn "failed to fetch update image!" + warn "url: $url" + return 1 +fi + +if ! unpack_img /tmp/updates.img /updates.tmp.$$; then + warn "failed to unpack update image!" + warn "url: $url" + return 1 +fi + +copytree /updates.tmp.$$ /updates + +mv /tmp/liveupdates.info /tmp/liveupdates.done diff --git a/modules.d/90livenet/livenet-generator.sh b/modules.d/90livenet/livenet-generator.sh new file mode 100755 index 0000000..3e9226b --- /dev/null +++ b/modules.d/90livenet/livenet-generator.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +[ -z "$root" ] && root=$(getarg root=) + +# support legacy syntax of passing liveimg and then just the base root +if getargbool 0 rd.live.image -d -y liveimg; then + liveroot="live:$root" +fi + +if [ "${root%%:*}" = "live" ]; then + liveroot=$root +fi + +[ "${liveroot%%:*}" = "live" ] || exit 0 + +case "$liveroot" in + live:nfs://* | nfs://*) + root="${root#live:}" + rootok=1 + ;; + live:http://* | http://*) + root="${root#live:}" + rootok=1 + ;; + live:https://* | https://*) + root="${root#live:}" + rootok=1 + ;; + live:ftp://* | ftp://*) + root="${root#live:}" + rootok=1 + ;; + live:torrent://* | torrent://*) + root="${root#live:}" + rootok=1 + ;; + live:tftp://* | tftp://*) + root="${root#live:}" + rootok=1 + ;; +esac + +[ "$rootok" != "1" ] && exit 0 + +GENERATOR_DIR="$2" +[ -z "$GENERATOR_DIR" ] && exit 1 + +[ -d "$GENERATOR_DIR" ] || mkdir -p "$GENERATOR_DIR" + +getargbool 0 rd.live.overlay.readonly -d -y readonly_overlay && readonly_overlay="--readonly" || readonly_overlay="" +getargbool 0 rd.live.overlay.overlayfs && overlayfs="yes" +[ -e /xor_overlayfs ] && xor_overlayfs="yes" +[ -e /xor_readonly ] && xor_readonly="--readonly" +ROOTFLAGS="$(getarg rootflags)" +{ + echo "[Unit]" + echo "Before=initrd-root-fs.target" + echo "[Mount]" + echo "Where=/sysroot" + if [ "$overlayfs$xor_overlayfs" = "yes" ]; then + echo "What=LiveOS_rootfs" + if [ "$readonly_overlay$xor_readonly" = "--readonly" ]; then + ovlfs=lowerdir=/run/overlayfs-r:/run/rootfsbase + else + ovlfs=lowerdir=/run/rootfsbase + fi + echo "Options=${ROOTFLAGS},${ovlfs},upperdir=/run/overlayfs,workdir=/run/ovlwork" + echo "Type=overlay" + _dev=LiveOS_rootfs + else + echo "What=/dev/mapper/live-rw" + [ -n "$ROOTFLAGS" ] && echo "Options=${ROOTFLAGS}" + _dev=$'dev-mapper-live\\x2drw' + fi +} > "$GENERATOR_DIR"/sysroot.mount + +mkdir -p "$GENERATOR_DIR/$_dev.device.d" +{ + echo "[Unit]" + echo "JobTimeoutSec=3000" + echo "JobRunningTimeoutSec=3000" +} > "$GENERATOR_DIR/$_dev.device.d/timeout.conf" diff --git a/modules.d/90livenet/livenetroot.sh b/modules.d/90livenet/livenetroot.sh new file mode 100755 index 0000000..66dd41b --- /dev/null +++ b/modules.d/90livenet/livenetroot.sh @@ -0,0 +1,60 @@ +#!/bin/sh +# livenetroot - fetch a live image from the network and run it + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +. /lib/url-lib.sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin +RETRIES=${RETRIES:-100} +SLEEP=${SLEEP:-5} + +[ -e /tmp/livenet.downloaded ] && exit 0 + +# args get passed from 40network/netroot +netroot="$2" +liveurl="${netroot#livenet:}" +info "fetching $liveurl" + +if getargbool 0 'rd.writable.fsimg'; then + + imgsize=$(($(curl -sIL "$liveurl" | sed -n 's/Content-Length: *\([[:digit:]]*\).*/\1/p') / (1024 * 1024))) + + check_live_ram $imgsize +fi + +imgfile= +#retry until the imgfile is populated with data or the max retries +i=1 +while [ "$i" -le "$RETRIES" ]; do + imgfile=$(fetch_url "$liveurl") + + # shellcheck disable=SC2181 + if [ $? != 0 ]; then + warn "failed to download live image: error $?" + imgfile= + fi + + if [ -n "$imgfile" -a -s "$imgfile" ]; then + break + else + if [ $i -ge "$RETRIES" ]; then + warn "failed to download live image after $i attempts." + exit 1 + fi + + sleep "$SLEEP" + fi + + i=$((i + 1)) +done > /tmp/livenet.downloaded + +# TODO: couldn't dmsquash-live-root handle this? +if [ "${imgfile##*.}" = "iso" ]; then + root=$(losetup -f) + losetup "$root" "$imgfile" +else + root=$imgfile +fi + +exec /sbin/dmsquash-live-root "$root" diff --git a/modules.d/90livenet/module-setup.sh b/modules.d/90livenet/module-setup.sh new file mode 100755 index 0000000..db0def5 --- /dev/null +++ b/modules.d/90livenet/module-setup.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# module-setup.sh for livenet + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + echo network url-lib dmsquash-live img-lib bash + return 0 +} + +# called by dracut +install() { + inst_hook cmdline 29 "$moddir/parse-livenet.sh" + inst_hook initqueue/online 95 "$moddir/fetch-liveupdate.sh" + inst_script "$moddir/livenetroot.sh" "/sbin/livenetroot" + if dracut_module_included "systemd-initrd"; then + inst_script "$moddir/livenet-generator.sh" "$systemdutildir"/system-generators/dracut-livenet-generator + fi + dracut_need_initqueue +} diff --git a/modules.d/90livenet/parse-livenet.sh b/modules.d/90livenet/parse-livenet.sh new file mode 100755 index 0000000..a1d14a8 --- /dev/null +++ b/modules.d/90livenet/parse-livenet.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# live net images - just like live images, but specified like: +# root=live:[url-to-backing-file] + +[ -z "$root" ] && root=$(getarg root=) +. /lib/url-lib.sh + +# live updates +updates=$(getarg live.updates=) +if [ -n "$updates" ]; then + # make sure network comes up even if we're doing a local live device + if [ -z "$netroot" ]; then + echo > /tmp/net.ifaces + fi + echo "$updates" > /tmp/liveupdates.info + echo '[ -e /tmp/liveupdates.done ]' > "$hookdir"/initqueue/finished/liveupdates.sh +fi + +str_starts "$root" "live:" && liveurl="$root" +str_starts "$liveurl" "live:" || return +liveurl="${liveurl#live:}" + +# setting netroot to "livenet:..." makes "livenetroot" get run after ifup +if get_url_handler "$liveurl" > /dev/null; then + info "livenet: root image at $liveurl" + netroot="livenet:$liveurl" + root="livenet" # quiet complaints from init + # shellcheck disable=SC2034 + rootok=1 + wait_for_dev -n /dev/root +else + info "livenet: no url handler for $liveurl" +fi diff --git a/modules.d/90lvm/64-lvm.rules b/modules.d/90lvm/64-lvm.rules new file mode 100644 index 0000000..1ad4911 --- /dev/null +++ b/modules.d/90lvm/64-lvm.rules @@ -0,0 +1,29 @@ +# hacky rules to try to activate lvm when we get new block devs... +# +# Copyright 2008, Red Hat, Inc. +# Jeremy Katz <katzj@redhat.com> + + +SUBSYSTEM!="block", GOTO="lvm_end" +ACTION!="add|change", GOTO="lvm_end" + +# If the md device is active (indicated by array_state), then set the flag +# LVM_MD_PV_ACTIVATED=1 indicating that the md device for the PV is ready +# to be used. The lvm udev rule running in root will check that this flag +# is set before it will process the md device (it wants to avoid +# processing an md device that exists but is not yet ready to be used.) +KERNEL=="md[0-9]*", ACTION=="change", ENV{ID_FS_TYPE}=="LVM2_member", ENV{LVM_MD_PV_ACTIVATED}!="1", TEST=="md/array_state", ENV{LVM_MD_PV_ACTIVATED}="1" + +# Also don't process disks that are slated to be a multipath device +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="lvm_end" +KERNEL=="dm-[0-9]*", ACTION=="add", GOTO="lvm_end" +ENV{ID_FS_TYPE}!="LVM?_member", GOTO="lvm_end" + +PROGRAM=="/bin/sh -c 'for i in $sys/$devpath/holders/dm-[0-9]*; do [ -e $$i ] && exit 0; done; exit 1;' ", \ + GOTO="lvm_end" + +RUN+="/sbin/initqueue --settled --onetime --unique /sbin/lvm_scan" +RUN+="/sbin/initqueue --timeout --name 51-lvm_scan --onetime --unique /sbin/lvm_scan --activationmode degraded" +RUN+="/bin/sh -c '>/tmp/.lvm_scan-%k;'" + +LABEL="lvm_end" diff --git a/modules.d/90lvm/lvm_scan.sh b/modules.d/90lvm/lvm_scan.sh new file mode 100755 index 0000000..980e449 --- /dev/null +++ b/modules.d/90lvm/lvm_scan.sh @@ -0,0 +1,176 @@ +#!/bin/sh + +# run lvm scan if udev has settled + +extraargs="$*" +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +VGS=$(getargs rd.lvm.vg -d rd_LVM_VG=) +LVS=$(getargs rd.lvm.lv -d rd_LVM_LV=) + +# shellcheck disable=SC2174 +[ -d /etc/lvm ] || mkdir -m 0755 -p /etc/lvm +[ -d /run/lvm ] || mkdir -m 0755 -p /run/lvm +# build a list of devices to scan +lvmdevs=$( + for f in /tmp/.lvm_scan-*; do + [ -e "$f" ] || continue + printf '%s' "${f##/tmp/.lvm_scan-} " + done +) + +check_lvm_ver() { + maj=$1 + min=$2 + ver=$3 + # --poll is supported since 2.2.57 + [ "$4" -lt "$maj" ] && return 1 + [ "$4" -gt "$maj" ] && return 0 + [ "$5" -lt "$min" ] && return 1 + [ "$5" -gt "$min" ] && return 0 + [ "$6" -ge "$ver" ] && return 0 + return 1 +} + +no_lvm_conf_filter() { + if [ ! -e /etc/lvm/lvm.conf ]; then + return 0 + fi + + if [ -e /run/lvm/initrd_no_filter ]; then + return 0 + fi + + if [ -e /run/lvm/initrd_filter ]; then + return 1 + fi + + if [ -e /run/lvm/initrd_global_filter ]; then + return 1 + fi + + # Save lvm config results in /run to avoid running + # lvm config commands for every PV that's scanned. + + filter=$(lvm config devices/filter 2> /dev/null | grep "$filter=") + if [ -n "$filter" ]; then + printf '%s\n' "$filter" > /run/lvm/initrd_filter + return 1 + fi + + global_filter=$(lvm config devices/global_filter 2> /dev/null | grep "$global_filter=") + if [ -n "$global_filter" ]; then + printf '%s\n' "$global_filter" > /run/lvm/initrd_global_filter + return 1 + fi + + # /etc/lvm/lvm.conf exists with no filter setting + true > /run/lvm/initrd_no_filter + return 0 +} + +# If no lvm.conf exists, create a basic one with a global section. +if [ ! -e /etc/lvm/lvm.conf ]; then + { + echo 'global {' + echo '}' + } > /etc/lvm/lvm.conf + lvmwritten=1 +fi + +# Save the original lvm.conf before appending a filter setting. +if [ ! -e /etc/lvm/lvm.conf.orig ]; then + cp /etc/lvm/lvm.conf /etc/lvm/lvm.conf.orig +fi + +# If the original lvm.conf does not contain a filter setting, +# then generate a filter and append it to the original lvm.conf. +# The filter is generated from the list PVs that have been seen +# so far (each has been processed by the lvm udev rule.) +if no_lvm_conf_filter; then + { + echo 'devices {' + printf ' filter = [ ' + for dev in $lvmdevs; do + printf '"a|^/dev/%s$|", ' "$dev" + done + echo '"r/.*/" ]' + echo '}' + } > /etc/lvm/lvm.conf.filter + lvmfilter=1 + cat /etc/lvm/lvm.conf.orig /etc/lvm/lvm.conf.filter > /etc/lvm/lvm.conf +fi + +# hopefully this output format will never change, e.g.: +# LVM version: 2.02.53(1) (2009-09-25) +OLDIFS=$IFS +IFS=. +# shellcheck disable=SC2046 +set -- $(lvm version 2> /dev/null) +IFS=$OLDIFS +maj=${1##*:} +min=$2 +sub=${3%% *} +sub=${sub%%\(*} + +# For lvchange and vgchange use --sysinit which: +# disables polling (--poll n) +# ignores monitoring (--ignoremonitoring) +# ignores locking failures (--ignorelockingfailure) +# disables hints (--nohints) +# +# For lvs and vgscan: +# disable locking (--nolocking) +# disable hints (--nohints) + +activate_args="--sysinit $extraargs" +unset extraargs + +export LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES=1 + +scan_args="--nolocking" + +check_lvm_ver 2 3 14 "$maj" "$min" "$sub" \ + && scan_args="$scan_args --nohints" + +if [ -n "$LVS" ]; then + info "Scanning devices $lvmdevs for LVM logical volumes $LVS" + # shellcheck disable=SC2086 + LVSLIST=$(lvm lvs $scan_args --noheading -o lv_full_name,segtype $LVS) + info "$LVSLIST" + + # Only attempt to activate an LV if it appears in the lvs output. + for LV in $LVS; do + if strstr "$LVSLIST" "$LV"; then + # This lvchange is expected to fail if all PVs used by + # the LV are not yet present. Premature/failed lvchange + # could be avoided by reporting if an LV is complete + # from the lvs command above and skipping this lvchange + # if the LV is not lised as complete. + # shellcheck disable=SC2086 + lvm lvchange --yes -K -ay $activate_args "$LV" 2>&1 | vinfo + fi + done +fi + +if [ -z "$LVS" ] || [ -n "$VGS" ]; then + info "Scanning devices $lvmdevs for LVM volume groups $VGS" + # shellcheck disable=SC2086 + lvm vgscan $scan_args 2>&1 | vinfo + # shellcheck disable=SC2086 + lvm vgchange -ay $activate_args $VGS 2>&1 | vinfo +fi + +if [ "$lvmwritten" ]; then + rm -f -- /etc/lvm/lvm.conf +elif [ "$lvmfilter" ]; then + # revert filter that was appended to existing lvm.conf + cp /etc/lvm/lvm.conf.orig /etc/lvm/lvm.conf + rm -f -- /etc/lvm/lvm.conf.filter +fi +unset lvmwritten +unset lvmfilter + +udevadm settle + +need_shutdown diff --git a/modules.d/90lvm/module-setup.sh b/modules.d/90lvm/module-setup.sh new file mode 100755 index 0000000..7ba4c69 --- /dev/null +++ b/modules.d/90lvm/module-setup.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# called by dracut +check() { + # No point trying to support lvm if the binaries are missing + require_binaries lvm grep || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for fs in "${host_fs_types[@]}"; do + [[ $fs == LVM*_member ]] && return 0 + done + return 255 + } + + return 0 +} + +# called by dracut +depends() { + # We depend on dm_mod being loaded + echo rootfs-block dm + return 0 +} + +# called by dracut +cmdline() { + local _activated + declare -A _activated + + for dev in "${!host_fs_types[@]}"; do + [[ -e /sys/block/${dev#/dev/}/dm/name ]] || continue + [[ -e /sys/block/${dev#/dev/}/dm/uuid ]] || continue + uuid=$(< "/sys/block/${dev#/dev/}/dm/uuid") + [[ ${uuid#LVM-} == "$uuid" ]] && continue + dev=$(< "/sys/block/${dev#/dev/}/dm/name") + eval "$(dmsetup splitname --nameprefixes --noheadings --rows "$dev" 2> /dev/null)" + [[ ${DM_VG_NAME} ]] && [[ ${DM_LV_NAME} ]] || return 1 + if ! [[ ${_activated[DM_VG_NAME / DM_LV_NAME]} ]]; then + printf " rd.lvm.lv=%s " "${DM_VG_NAME}/${DM_LV_NAME} " + _activated["${DM_VG_NAME}/${DM_LV_NAME}"]=1 + fi + done +} + +installkernel() { + hostonly='' dracut_instmods -o -P ".*/(bcache/|md-cluster).*" "=drivers/md" +} + +# called by dracut +install() { + inst_multiple lvm grep + + if [[ $hostonly_cmdline == "yes" ]]; then + local _lvmconf + _lvmconf=$(cmdline) + [[ $_lvmconf ]] && printf "%s\n" "$_lvmconf" >> "${initdir}/etc/cmdline.d/90lvm.conf" + fi + + inst_rules "$moddir/64-lvm.rules" + + if [[ $hostonly ]] || [[ $lvmconf == "yes" ]]; then + if [[ -f $dracutsysrootdir/etc/lvm/lvm.conf ]]; then + inst_simple -H /etc/lvm/lvm.conf + fi + + export LVM_SUPPRESS_FD_WARNINGS=1 + # Also install any files needed for LVM system id support. + if [[ -f $dracutsysrootdir/etc/lvm/lvmlocal.conf ]]; then + inst_simple -H /etc/lvm/lvmlocal.conf + fi + eval "$(lvm dumpconfig global/system_id_source &> /dev/null)" + if [ "$system_id_source" == "file" ]; then + eval "$(lvm dumpconfig global/system_id_file)" + if [ -f "$system_id_file" ]; then + inst_simple -H "$system_id_file" + fi + fi + unset LVM_SUPPRESS_FD_WARNINGS + fi + + inst_rules 11-dm-lvm.rules + + # Gentoo ebuild for LVM2 prior to 2.02.63-r1 doesn't install above rules + # files, but provides the one below: + inst_rules 64-device-mapper.rules + # debian udev rules + inst_rules 56-lvm.rules 60-persistent-storage-lvm.rules + + inst_script "$moddir/lvm_scan.sh" /sbin/lvm_scan + inst_hook cmdline 30 "$moddir/parse-lvm.sh" + + if [[ $hostonly ]] && find_binary lvs &> /dev/null; then + for dev in "${!host_fs_types[@]}"; do + [[ -e /sys/block/${dev#/dev/}/dm/name ]] || continue + dev=$(< "/sys/block/${dev#/dev/}/dm/name") + eval "$(dmsetup splitname --nameprefixes --noheadings --rows "$dev" 2> /dev/null)" + # shellcheck disable=SC2015 + [[ ${DM_VG_NAME} ]] && [[ ${DM_LV_NAME} ]] || continue + case "$(lvs --noheadings -o segtype "${DM_VG_NAME}" 2> /dev/null)" in + *thin* | *cache* | *era*) + inst_multiple -o thin_dump thin_restore thin_check thin_repair \ + cache_dump cache_restore cache_check cache_repair \ + era_check era_dump era_invalidate era_restore + break + ;; + esac + done + fi + + if ! [[ $hostonly ]]; then + inst_multiple -o thin_dump thin_restore thin_check thin_repair \ + cache_dump cache_restore cache_check cache_repair \ + era_check era_dump era_invalidate era_restore + fi + + dracut_need_initqueue +} diff --git a/modules.d/90lvm/parse-lvm.sh b/modules.d/90lvm/parse-lvm.sh new file mode 100755 index 0000000..d774882 --- /dev/null +++ b/modules.d/90lvm/parse-lvm.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +if [ -e /etc/lvm/lvm.conf ] && ! getargbool 1 rd.lvm.conf -d -n rd_NO_LVMCONF; then + rm -f -- /etc/lvm/lvm.conf +fi + +LV_DEVS="$(getargs rd.lvm.vg -d rd_LVM_VG=) $(getargs rd.lvm.lv -d rd_LVM_LV=)" + +if ! getargbool 1 rd.lvm -d -n rd_NO_LVM \ + || { [ -z "$LV_DEVS" ] && ! getargbool 0 rd.auto; }; then + info "rd.lvm=0: removing LVM activation" + rm -f -- /etc/udev/rules.d/64-lvm*.rules +else + for dev in $LV_DEVS; do + wait_for_dev -n "/dev/$dev" + done +fi diff --git a/modules.d/90mdraid/59-persistent-storage-md.rules b/modules.d/90mdraid/59-persistent-storage-md.rules new file mode 100644 index 0000000..0d745cc --- /dev/null +++ b/modules.d/90mdraid/59-persistent-storage-md.rules @@ -0,0 +1,24 @@ +SUBSYSTEM!="block", GOTO="md_end" +ACTION!="add|change", GOTO="md_end" +# Also don't process disks that are slated to be a multipath device +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="md_end" + +KERNEL!="md[0-9]*|md_d[0-9]*|md/*", KERNEL!="md*", GOTO="md_end" + +# partitions have no md/{array_state,metadata_version} +ENV{DEVTYPE}=="partition", GOTO="md_ignore_state" + +# container devices have a metadata version of e.g. 'external:ddf' and +# never leave state 'inactive' +ATTR{md/metadata_version}=="external:[A-Za-z]*", ATTR{md/array_state}=="inactive", GOTO="md_ignore_state" +TEST!="md/array_state", GOTO="md_end" +ATTR{md/array_state}=="|clear|inactive", GOTO="md_end" + +LABEL="md_ignore_state" + +IMPORT{program}="/sbin/mdadm --detail --export $tempnode" +IMPORT{builtin}="blkid" +OPTIONS+="link_priority=100" +OPTIONS+="watch" +OPTIONS+="db_persist" +LABEL="md_end" diff --git a/modules.d/90mdraid/65-md-incremental-imsm.rules b/modules.d/90mdraid/65-md-incremental-imsm.rules new file mode 100644 index 0000000..6697f15 --- /dev/null +++ b/modules.d/90mdraid/65-md-incremental-imsm.rules @@ -0,0 +1,44 @@ +# This file causes block devices with Linux RAID (mdadm) signatures to +# automatically cause mdadm to be run. +# See udev(8) for syntax + +ACTION!="add|change", GOTO="md_end" +SUBSYSTEM!="block", GOTO="md_end" +ENV{rd_NO_MD}=="?*", GOTO="md_end" +KERNEL=="md*", ENV{ID_FS_TYPE}!="linux_raid_member", GOTO="md_end" +KERNEL=="md*", ACTION!="change", GOTO="md_end" + +# Also don't process disks that are slated to be a multipath device +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="md_end" + +ENV{ID_FS_TYPE}=="ddf_raid_member|isw_raid_member|linux_raid_member", GOTO="md_try" +GOTO="md_end" + +LABEL="md_try" +ENV{ID_FS_TYPE}=="isw_raid_member", ENV{rd_NO_MDIMSM}=="?*", GOTO="md_end" +ENV{ID_FS_TYPE}=="ddf_raid_member", ENV{rd_NO_MDDDF}=="?*", GOTO="md_end" + +# already done ? +PROGRAM="/bin/sh -c 'for i in $sys/$devpath/holders/md[0-9_]*; do [ -e $$i ] && exit 0; done; exit 1;' ", \ + GOTO="md_end" + +# for native arrays - array's uuid has to be specified +# for containers - container's uuid has to be specified +# TODO : how to get embedded array's uuid having container's component ? +# +# UUID CHECK + +ENV{DEVTYPE}!="partition", \ + RUN+="/sbin/partx -d --nr 1-1024 $env{DEVNAME}" + +RUN+="/sbin/initqueue --timeout --name 50-mdraid_start --onetime --unique /sbin/mdraid_start" + +# +# Incrementally build the md array; this will automatically assemble +# any eventual containers as well (imsm, ddf) +# +LABEL="md_incremental" + +RUN+="/sbin/mdadm -I $env{DEVNAME}" + +LABEL="md_end" diff --git a/modules.d/90mdraid/md-shutdown.sh b/modules.d/90mdraid/md-shutdown.sh new file mode 100755 index 0000000..ca768a9 --- /dev/null +++ b/modules.d/90mdraid/md-shutdown.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +_do_md_shutdown() { + local ret + local final="$1" + info "Waiting for mdraid devices to be clean." + mdadm -vv --wait-clean --scan | vinfo + ret=$? + info "Disassembling mdraid devices." + mdadm -vv --stop --scan | vinfo + ret=$((ret + $?)) + if [ "x$final" != "x" ]; then + info "/proc/mdstat:" + vinfo < /proc/mdstat + fi + return $ret +} + +if command -v mdadm > /dev/null; then + _do_md_shutdown "$1" +else + : +fi diff --git a/modules.d/90mdraid/mdmon-pre-shutdown.sh b/modules.d/90mdraid/mdmon-pre-shutdown.sh new file mode 100755 index 0000000..a5cd850 --- /dev/null +++ b/modules.d/90mdraid/mdmon-pre-shutdown.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +_do_mdmon_takeover() { + local ret + mdmon --takeover --all + ret=$? + [ $ret -eq 0 ] && info "Taking over mdmon processes." + return $ret +} + +if command -v mdmon > /dev/null; then + _do_mdmon_takeover "$1" +fi diff --git a/modules.d/90mdraid/mdmon-pre-udev.sh b/modules.d/90mdraid/mdmon-pre-udev.sh new file mode 100755 index 0000000..b15cca8 --- /dev/null +++ b/modules.d/90mdraid/mdmon-pre-udev.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# save state dir for mdmon/mdadm for the real root +[ -d /run/mdadm ] || mkdir -m 0755 -p /run/mdadm +# backward compat link diff --git a/modules.d/90mdraid/mdraid-cleanup.sh b/modules.d/90mdraid/mdraid-cleanup.sh new file mode 100755 index 0000000..ce50733 --- /dev/null +++ b/modules.d/90mdraid/mdraid-cleanup.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +containers="" +for md in /dev/md[0-9_]*; do + [ -b "$md" ] || continue + udevinfo="$(udevadm info --query=property --name="$md")" + strstr "$udevinfo" "DEVTYPE=partition" && continue + if strstr "$udevinfo" "MD_LEVEL=container"; then + containers="$containers $md" + continue + fi + mdadm -S "$md" > /dev/null 2>&1 +done + +for md in $containers; do + mdadm -S "$md" > /dev/null 2>&1 +done + +unset containers udevinfo diff --git a/modules.d/90mdraid/mdraid-needshutdown.sh b/modules.d/90mdraid/mdraid-needshutdown.sh new file mode 100755 index 0000000..f248c4b --- /dev/null +++ b/modules.d/90mdraid/mdraid-needshutdown.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +for md in /dev/md[0-9_]*; do + [ -b "$md" ] || continue + need_shutdown + break +done diff --git a/modules.d/90mdraid/mdraid-waitclean.sh b/modules.d/90mdraid/mdraid-waitclean.sh new file mode 100755 index 0000000..9317962 --- /dev/null +++ b/modules.d/90mdraid/mdraid-waitclean.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +if getargbool 0 rd.md.waitclean; then + type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + containers="" + for md in /dev/md[0-9_]*; do + [ -b "$md" ] || continue + udevinfo="$(udevadm info --query=property --name="$md")" + strstr "$udevinfo" "DEVTYPE=partition" && continue + if strstr "$udevinfo" "MD_LEVEL=container"; then + containers="$containers $md" + continue + fi + info "Waiting for $md to become clean" + mdadm -W "$md" > /dev/null 2>&1 + done + + for md in $containers; do + info "Waiting for $md to become clean" + mdadm -W "$md" > /dev/null 2>&1 + done + + unset containers udevinfo +fi diff --git a/modules.d/90mdraid/mdraid_start.sh b/modules.d/90mdraid/mdraid_start.sh new file mode 100755 index 0000000..d8c5de2 --- /dev/null +++ b/modules.d/90mdraid/mdraid_start.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +type getargs > /dev/null 2>&1 || . /lib/dracut-lib.sh + +_md_start() { + local _udevinfo + local _path_s + local _path_d + local _md="$1" + + _udevinfo="$(udevadm info --query=property --name="${_md}")" + strstr "$_udevinfo" "MD_LEVEL=container" && return 0 + strstr "$_udevinfo" "DEVTYPE=partition" && return 0 + + _path_s="/sys/$(udevadm info -q path -n "${_md}")/md/array_state" + [ ! -r "$_path_s" ] && return 0 + + # inactive ? + [ "$(cat "$_path_s")" != "inactive" ] && return 0 + + mdadm -R "${_md}" 2>&1 | vinfo + + # still inactive ? + [ "$(cat "$_path_s")" = "inactive" ] && return 0 + + _path_d="${_path_s%/*}/degraded" + [ ! -r "$_path_d" ] && return 0 + : > "$hookdir"/initqueue/work +} + +_md_force_run() { + local _md + local _UUID + local _MD_UUID + + _MD_UUID=$(getargs rd.md.uuid -d rd_MD_UUID=) + [ -n "$_MD_UUID" ] || getargbool 0 rd.auto || return + + if [ -n "$_MD_UUID" ]; then + _MD_UUID=$(str_replace "$_MD_UUID" "-" "") + _MD_UUID=$(str_replace "$_MD_UUID" ":" "") + + for _md in /dev/md[0-9_]*; do + [ -b "$_md" ] || continue + _UUID=$( + /sbin/mdadm -D --export "$_md" \ + | while read -r line || [ -n "$line" ]; do + str_starts "$line" "MD_UUID=" || continue + printf "%s" "${line#MD_UUID=}" + done + ) + + [ -z "$_UUID" ] && continue + _UUID=$(str_replace "$_UUID" ":" "") + + # check if we should handle this device + strstr "$_MD_UUID" "$_UUID" || continue + + _md_start "${_md}" + done + else + # try to force-run anything not running yet + for _md in /dev/md[0-9_]*; do + [ -b "$_md" ] || continue + _md_start "${_md}" + done + fi +} + +_md_force_run diff --git a/modules.d/90mdraid/module-setup.sh b/modules.d/90mdraid/module-setup.sh new file mode 100755 index 0000000..6179a98 --- /dev/null +++ b/modules.d/90mdraid/module-setup.sh @@ -0,0 +1,139 @@ +#!/bin/bash + +# called by dracut +check() { + local dev holder + + # No mdadm? No mdraid support. + require_binaries mdadm expr || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for dev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$dev]} != *_raid_member ]] && continue + + DEVPATH=$(get_devpath_block "$dev") + + for holder in "$DEVPATH"/holders/*; do + [[ -e $holder ]] || continue + [[ -e "$holder/md" ]] && return 0 + break + done + + done + return 255 + } + + return 0 +} + +# called by dracut +depends() { + echo rootfs-block + return 0 +} + +# called by dracut +installkernel() { + instmods '=drivers/md' +} + +# called by dracut +cmdline() { + local _activated dev line UUID + declare -A _activated + + for dev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$dev]} != *_raid_member ]] && continue + + UUID=$( + /sbin/mdadm --examine --export "$dev" \ + | while read -r line || [[ "$line" ]]; do + [[ ${line#MD_UUID=} == "$line" ]] && continue + printf "%s" "${line#MD_UUID=} " + done + ) + + [[ -z $UUID ]] && continue + + if ! [[ ${_activated[${UUID}]} ]]; then + printf "%s" " rd.md.uuid=${UUID}" + _activated["${UUID}"]=1 + fi + + done +} + +# called by dracut +install() { + local rule rule_path + inst_multiple cat expr + inst_multiple -o mdmon + inst "$(command -v partx)" /sbin/partx + inst "$(command -v mdadm)" /sbin/mdadm + + if [[ $hostonly_cmdline == "yes" ]]; then + local _raidconf + _raidconf=$(cmdline) + [[ $_raidconf ]] && printf "%s\n" "$_raidconf" >> "${initdir}/etc/cmdline.d/90mdraid.conf" + fi + + # <mdadm-3.3 udev rule + inst_rules 64-md-raid.rules + # >=mdadm-3.3 udev rules + inst_rules 63-md-raid-arrays.rules 64-md-raid-assembly.rules + # remove incremental assembly from stock rules, so they don't shadow + # 65-md-inc*.rules and its fine-grained controls, or cause other problems + # when we explicitly don't want certain components to be incrementally + # assembled + for rule in 64-md-raid.rules 64-md-raid-assembly.rules; do + rule_path="${initdir}${udevdir}/rules.d/${rule}" + # shellcheck disable=SC2016 + [ -f "${rule_path}" ] && sed -i -r \ + -e '/(RUN|IMPORT\{program\})\+?="[[:alpha:]/]*mdadm[[:blank:]]+(--incremental|-I)[[:blank:]]+(--export )?(\$env\{DEVNAME\}|\$tempnode|\$devnode)/d' \ + "${rule_path}" + done + + inst_rules "$moddir/65-md-incremental-imsm.rules" + + inst_rules "$moddir/59-persistent-storage-md.rules" + + if [[ $hostonly ]] || [[ $mdadmconf == "yes" ]]; then + if [[ -f $dracutsysrootdir/etc/mdadm.conf ]]; then + inst -H /etc/mdadm.conf + else + [[ -f $dracutsysrootdir/etc/mdadm/mdadm.conf ]] && inst -H /etc/mdadm/mdadm.conf /etc/mdadm.conf + fi + if [[ -d $dracutsysrootdir/etc/mdadm.conf.d ]]; then + local f + inst_dir /etc/mdadm.conf.d + for f in /etc/mdadm.conf.d/*.conf; do + [[ -f "$dracutsysrootdir$f" ]] || continue + inst -H "$f" + done + fi + fi + + inst_hook pre-udev 30 "$moddir/mdmon-pre-udev.sh" + inst_hook pre-trigger 30 "$moddir/parse-md.sh" + inst_hook pre-mount 10 "$moddir/mdraid-waitclean.sh" + inst_hook cleanup 99 "$moddir/mdraid-needshutdown.sh" + inst_hook shutdown 30 "$moddir/md-shutdown.sh" + inst_script "$moddir/mdraid-cleanup.sh" /sbin/mdraid-cleanup + inst_script "$moddir/mdraid_start.sh" /sbin/mdraid_start + if dracut_module_included "systemd"; then + if [[ -e $dracutsysrootdir$systemdsystemunitdir/mdmon@.service ]]; then + inst_simple "$systemdsystemunitdir"/mdmon@.service + fi + if [[ -e $dracutsysrootdir$systemdsystemunitdir/mdadm-last-resort@.service ]]; then + inst_simple "$systemdsystemunitdir"/mdadm-last-resort@.service + fi + if [[ -e $dracutsysrootdir$systemdsystemunitdir/mdadm-last-resort@.timer ]]; then + inst_simple "$systemdsystemunitdir"/mdadm-last-resort@.timer + fi + if [[ -e $dracutsysrootdir$systemdsystemunitdir/mdadm-grow-continue@.service ]]; then + inst_simple "$systemdsystemunitdir"/mdadm-grow-continue@.service + fi + fi + inst_hook pre-shutdown 30 "$moddir/mdmon-pre-shutdown.sh" + dracut_need_initqueue +} diff --git a/modules.d/90mdraid/parse-md.sh b/modules.d/90mdraid/parse-md.sh new file mode 100755 index 0000000..4d3a6b2 --- /dev/null +++ b/modules.d/90mdraid/parse-md.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# we really need to use `expr substr` with dash +# shellcheck disable=SC2003 disable=SC2308 + +MD_UUID=$(getargs rd.md.uuid -d rd_MD_UUID=) +# normalize the uuid +MD_UUID=$(str_replace "$MD_UUID" "-" "") +MD_UUID=$(str_replace "$MD_UUID" ":" "") + +if { [ -z "$MD_UUID" ] && ! getargbool 0 rd.auto; } || ! getargbool 1 rd.md -d -n rd_NO_MD; then + info "rd.md=0: removing MD RAID activation" + udevproperty rd_NO_MD=1 +else + # rewrite the md rules to only process the specified raid array + if [ -n "$MD_UUID" ]; then + for f in /etc/udev/rules.d/65-md-incremental*.rules; do + [ -e "$f" ] || continue + while read -r line || [ -n "$line" ]; do + if [ "${line%%UUID CHECK}" != "$line" ]; then + for uuid in $MD_UUID; do + printf 'ENV{ID_FS_UUID}=="%s", GOTO="md_uuid_ok"\n' "$(expr substr "$uuid" 1 8)-$(expr substr "$uuid" 9 4)-$(expr substr "$uuid" 13 4)-$(expr substr "$uuid" 17 4)-$(expr substr "$uuid" 21 12)" + done + # shellcheck disable=SC2016 + printf 'IMPORT{program}="/sbin/mdadm --examine --export $tempnode"\n' + for uuid in $MD_UUID; do + printf 'ENV{MD_UUID}=="%s", GOTO="md_uuid_ok"\n' "$(expr substr "$uuid" 1 8):$(expr substr "$uuid" 9 8):$(expr substr "$uuid" 17 8):$(expr substr "$uuid" 25 8)" + done + printf 'GOTO="md_end"\n' + printf 'LABEL="md_uuid_ok"\n' + else + echo "$line" + fi + done < "${f}" > "${f}.new" + mv "${f}.new" "$f" + done + for uuid in $MD_UUID; do + uuid="$(expr substr "$uuid" 1 8):$(expr substr "$uuid" 9 8):$(expr substr "$uuid" 17 8):$(expr substr "$uuid" 25 8)" + wait_for_dev "/dev/disk/by-id/md-uuid-${uuid}" + done + fi +fi + +if [ -e /etc/mdadm.conf ] && getargbool 1 rd.md.conf -d -n rd_NO_MDADMCONF; then + udevproperty rd_MDADMCONF=1 + rm -f -- "$hookdir"/pre-pivot/*mdraid-cleanup.sh +fi + +if ! getargbool 1 rd.md.conf -d -n rd_NO_MDADMCONF; then + rm -f -- /etc/mdadm/mdadm.conf /etc/mdadm.conf + ln -s "$(command -v mdraid-cleanup)" "$hookdir"/pre-pivot/31-mdraid-cleanup.sh 2> /dev/null +fi + +# noiswmd nodmraid for anaconda / rc.sysinit compatibility +# note nodmraid really means nobiosraid, so we don't want MDIMSM then either +if ! getargbool 1 rd.md.imsm -d -n rd_NO_MDIMSM -n noiswmd -n nodmraid; then + info "no MD RAID for imsm/isw raids" + udevproperty rd_NO_MDIMSM=1 +fi + +# same thing with ddf containers +if ! getargbool 1 rd.md.ddf -n rd_NO_MDDDF -n noddfmd -n nodmraid; then + info "no MD RAID for SNIA ddf raids" + udevproperty rd_NO_MDDDF=1 +fi diff --git a/modules.d/90multipath/module-setup.sh b/modules.d/90multipath/module-setup.sh new file mode 100755 index 0000000..9c3e629 --- /dev/null +++ b/modules.d/90multipath/module-setup.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +is_mpath() { + local _dev=$1 + [ -e /sys/dev/block/"$_dev"/dm/uuid ] || return 1 + [[ $(cat /sys/dev/block/"$_dev"/dm/uuid) =~ mpath- ]] && return 0 + return 1 +} + +majmin_to_mpath_dev() { + local _dev + for i in /dev/mapper/*; do + [[ $i == /dev/mapper/control ]] && continue + _dev=$(get_maj_min "$i") + if [ "$_dev" = "$1" ]; then + echo "$i" + return + fi + done +} + +# called by dracut +check() { + [[ $hostonly ]] || [[ $mount_needs ]] && { + for_each_host_dev_and_slaves is_mpath || return 255 + } + + # if there's no multipath binary, no go. + require_binaries multipath || return 1 + require_binaries kpartx || return 1 + + return 0 +} + +# called by dracut +depends() { + echo rootfs-block + echo dm + return 0 +} + +# called by dracut +cmdline() { + for m in scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm_multipath; do + if grep -m 1 -q "$m" /proc/modules; then + printf 'rd.driver.pre=%s ' "$m" + fi + done +} + +# called by dracut +installkernel() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + local _funcs='scsi_register_device_handler|dm_dirty_log_type_register|dm_register_path_selector|dm_register_target' + + if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then + _s390drivers="=drivers/s390/scsi" + fi + + hostonly='' dracut_instmods -o -s "$_funcs" "=drivers/scsi" "=drivers/md" ${_s390drivers:+"$_s390drivers"} +} + +mpathconf_installed() { + command -v mpathconf &> /dev/null +} + +# called by dracut +install() { + local -A _allow + local config_dir + + add_hostonly_mpath_conf() { + if is_mpath "$1"; then + local _dev + + _dev=$(majmin_to_mpath_dev "$1") + [ -z "$_dev" ] && return + _allow["$_dev"]="$_dev" + fi + } + + local k v + while read -r k v; do + if [[ $k == "config_dir" ]]; then + v="${v#\"}" + config_dir="${v%\"}" + break + fi + done < <(multipath -t 2> /dev/null) + [[ -d $config_dir ]] || config_dir=/etc/multipath/conf.d + + inst_multiple \ + pkill \ + kpartx \ + dmsetup \ + multipath \ + multipathd + + inst_multiple -o \ + mpath_wait \ + mpathconf \ + mpathpersist \ + xdrgetprio \ + xdrgetuid \ + /etc/xdrdevices.conf \ + /etc/multipath.conf \ + /etc/multipath/* \ + "$config_dir"/* \ + "$tmpfilesdir/multipath.conf" + + mpathconf_installed \ + && [[ $hostonly ]] && [[ $hostonly_mode == "strict" ]] && { + for_each_host_dev_and_slaves_all add_hostonly_mpath_conf + if ((${#_allow[@]} > 0)); then + local -a _args + local _dev + for _dev in "${_allow[@]}"; do + _args+=("--allow" "$_dev") + done + mpathconf "${_args[@]}" --outfile "${initdir}"/etc/multipath.conf + fi + } + + inst "$(command -v partx)" /sbin/partx + + inst_libdir_file "libmultipath*" "multipath/*" + inst_libdir_file 'libgcc_s.so*' + + if [[ $hostonly_cmdline ]]; then + local _conf + _conf=$(cmdline) + [[ $_conf ]] && echo "$_conf" >> "${initdir}/etc/cmdline.d/90multipath.conf" + fi + + if dracut_module_included "systemd"; then + if mpathconf_installed; then + inst_simple "${moddir}/multipathd-configure.service" "${systemdsystemunitdir}/multipathd-configure.service" + $SYSTEMCTL -q --root "$initdir" enable multipathd-configure.service + fi + inst_simple "${moddir}/multipathd.service" "${systemdsystemunitdir}/multipathd.service" + $SYSTEMCTL -q --root "$initdir" enable multipathd.service + else + inst_hook pre-trigger 02 "$moddir/multipathd.sh" + inst_hook cleanup 02 "$moddir/multipathd-stop.sh" + fi + + inst_hook cleanup 80 "$moddir/multipathd-needshutdown.sh" + inst_hook shutdown 20 "$moddir/multipath-shutdown.sh" + + inst_rules 40-multipath.rules 56-multipath.rules \ + 62-multipath.rules 65-multipath.rules \ + 66-kpartx.rules 67-kpartx-compat.rules \ + 11-dm-mpath.rules 11-dm-parts.rules +} diff --git a/modules.d/90multipath/multipath-shutdown.sh b/modules.d/90multipath/multipath-shutdown.sh new file mode 100755 index 0000000..220da8f --- /dev/null +++ b/modules.d/90multipath/multipath-shutdown.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +for i in $(multipath -l -v1); do + if ! dmsetup table "$i" | sed -n '/.*queue_if_no_path.*/q1'; then + dmsetup message "$i" 0 fail_if_no_path + fi +done diff --git a/modules.d/90multipath/multipathd-configure.service b/modules.d/90multipath/multipathd-configure.service new file mode 100644 index 0000000..a2baec7 --- /dev/null +++ b/modules.d/90multipath/multipathd-configure.service @@ -0,0 +1,21 @@ +[Unit] +Description=Device-Mapper Multipath Default Configuration +Before=iscsi.service iscsid.service lvm2-activation-early.service +Wants=systemd-udev-trigger.service systemd-udev-settle.service local-fs-pre.target +After=systemd-udev-trigger.service systemd-udev-settle.service +Before=local-fs-pre.target multipathd.service +DefaultDependencies=no +Conflicts=shutdown.target + +ConditionKernelCommandLine=rd.multipath=default +ConditionPathExists=!/etc/multipath.conf + +[Service] +Type=oneshot +RemainAfterExit=yes +# mpathconf requires /etc/multipath to already exist +ExecStartPre=-/usr/bin/mkdir -p /etc/multipath +ExecStart=/usr/sbin/mpathconf --enable + +[Install] +WantedBy=sysinit.target diff --git a/modules.d/90multipath/multipathd-needshutdown.sh b/modules.d/90multipath/multipathd-needshutdown.sh new file mode 100755 index 0000000..6dc68bb --- /dev/null +++ b/modules.d/90multipath/multipathd-needshutdown.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +type need_shutdown > /dev/null 2>&1 || . /lib/dracut-lib.sh + +for i in $(multipath -l -v1); do + if dmsetup table "$i" | sed -n '/.*queue_if_no_path.*/q1'; then + need_shutdown + break + fi +done diff --git a/modules.d/90multipath/multipathd-stop.sh b/modules.d/90multipath/multipathd-stop.sh new file mode 100755 index 0000000..05181b4 --- /dev/null +++ b/modules.d/90multipath/multipathd-stop.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +type pidof > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if [ -e /etc/multipath.conf ]; then + pkill multipathd > /dev/null 2>&1 + + if pidof multipathd > /dev/null 2>&1; then + sleep 0.2 + fi + + if pidof multipathd > /dev/null 2>&1; then + pkill -9 multipathd > /dev/null 2>&1 + fi +fi diff --git a/modules.d/90multipath/multipathd.service b/modules.d/90multipath/multipathd.service new file mode 100644 index 0000000..1680cdf --- /dev/null +++ b/modules.d/90multipath/multipathd.service @@ -0,0 +1,27 @@ +[Unit] +Description=Device-Mapper Multipath Device Controller +Before=lvm2-activation-early.service +Before=local-fs-pre.target blk-availability.service shutdown.target +Wants=systemd-udevd-kernel.socket +After=systemd-udevd-kernel.socket +After=multipathd.socket systemd-remount-fs.service +Before=initrd-cleanup.service +DefaultDependencies=no +Conflicts=shutdown.target +Conflicts=initrd-cleanup.service +ConditionKernelCommandLine=!nompath +ConditionKernelCommandLine=!rd.multipath=0 +ConditionKernelCommandLine=!rd_NO_MULTIPATH +ConditionKernelCommandLine=!multipath=off +ConditionVirtualization=!container + +[Service] +Type=notify +NotifyAccess=main +ExecStartPre=-/sbin/modprobe dm-multipath +ExecStart=/sbin/multipathd -d -s +ExecReload=/sbin/multipathd reconfigure +TasksMax=infinity + +[Install] +WantedBy=sysinit.target diff --git a/modules.d/90multipath/multipathd.sh b/modules.d/90multipath/multipathd.sh new file mode 100755 index 0000000..1e26c1d --- /dev/null +++ b/modules.d/90multipath/multipathd.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if [ "$(getarg rd.multipath)" = "default" ] && [ ! -e /etc/multipath.conf ]; then + # mpathconf requires /etc/multipath to already exist + mkdir -p /etc/multipath + mpathconf --enable +fi + +if getargbool 1 rd.multipath -d -n rd_NO_MULTIPATH && [ -e /etc/multipath.conf ]; then + modprobe dm-multipath + multipathd -B || multipathd + need_shutdown +else + rm -- /etc/udev/rules.d/??-multipath.rules 2> /dev/null +fi diff --git a/modules.d/90nvdimm/module-setup.sh b/modules.d/90nvdimm/module-setup.sh new file mode 100755 index 0000000..8d33610 --- /dev/null +++ b/modules.d/90nvdimm/module-setup.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# called by dracut +check() { + if [[ ! $hostonly ]]; then + return 0 + fi + [[ $DRACUT_KERNEL_MODALIASES && -f $DRACUT_KERNEL_MODALIASES ]] \ + && grep -q libnvdimm "$DRACUT_KERNEL_MODALIASES" && return 0 + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + # Directories to search for NVDIMM "providers" (firmware drivers) + # These modules call "nvdimm_bus_register()". + #instmods() will take care of hostonly + dracut_instmods -o -s nvdimm_bus_register \ + '=drivers/nvdimm' \ + '=drivers/acpi' \ + '=arch/powerpc' +} + +# called by dracut +install() { + inst_multiple -o ndctl /etc/ndctl/keys/tpm.handle "/etc/ndctl/keys/*.blob" +} diff --git a/modules.d/90overlayfs/module-setup.sh b/modules.d/90overlayfs/module-setup.sh new file mode 100755 index 0000000..893e2dc --- /dev/null +++ b/modules.d/90overlayfs/module-setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +check() { + [[ $hostonly ]] && return 1 + return 255 +} + +depends() { + echo base +} + +installkernel() { + instmods overlay +} + +install() { + inst_hook mount 01 "$moddir/mount-overlayfs.sh" + inst_hook pre-mount 01 "$moddir/prepare-overlayfs.sh" +} diff --git a/modules.d/90overlayfs/mount-overlayfs.sh b/modules.d/90overlayfs/mount-overlayfs.sh new file mode 100755 index 0000000..e1d23fb --- /dev/null +++ b/modules.d/90overlayfs/mount-overlayfs.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +getargbool 0 rd.live.overlay.overlayfs && overlayfs="yes" +getargbool 0 rd.live.overlay.readonly -d -y readonly_overlay && readonly_overlay="--readonly" || readonly_overlay="" + +ROOTFLAGS="$(getarg rootflags)" + +if [ -n "$overlayfs" ]; then + if [ -n "$readonly_overlay" ] && [ -h /run/overlayfs-r ]; then + ovlfs=lowerdir=/run/overlayfs-r:/run/rootfsbase + else + ovlfs=lowerdir=/run/rootfsbase + fi + + if ! strstr "$(cat /proc/mounts)" LiveOS_rootfs; then + mount -t overlay LiveOS_rootfs -o "$ROOTFLAGS,$ovlfs",upperdir=/run/overlayfs,workdir=/run/ovlwork "$NEWROOT" + fi +fi diff --git a/modules.d/90overlayfs/prepare-overlayfs.sh b/modules.d/90overlayfs/prepare-overlayfs.sh new file mode 100755 index 0000000..87bcc19 --- /dev/null +++ b/modules.d/90overlayfs/prepare-overlayfs.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +getargbool 0 rd.live.overlay.overlayfs && overlayfs="yes" +getargbool 0 rd.live.overlay.reset -d -y reset_overlay && reset_overlay="yes" + +if [ -n "$overlayfs" ]; then + if ! [ -e /run/rootfsbase ]; then + mkdir -m 0755 -p /run/rootfsbase + mount --bind "$NEWROOT" /run/rootfsbase + fi + + mkdir -m 0755 -p /run/overlayfs + mkdir -m 0755 -p /run/ovlwork + if [ -n "$reset_overlay" ] && [ -h /run/overlayfs ]; then + ovlfsdir=$(readlink /run/overlayfs) + info "Resetting the OverlayFS overlay directory." + rm -r -- "${ovlfsdir:?}"/* "${ovlfsdir:?}"/.* > /dev/null 2>&1 + fi +fi diff --git a/modules.d/90ppcmac/load-thermal.sh b/modules.d/90ppcmac/load-thermal.sh new file mode 100755 index 0000000..72f2581 --- /dev/null +++ b/modules.d/90ppcmac/load-thermal.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# This hook attempts to load the appropriate thermal modules +# for PowerPC Macs depending on the specific machine you have. + +[ -r /proc/cpuinfo ] || exit 0 + +load_windfarm() { + local pm_model + pm_model="$(sed -n '/model/p' /proc/cpuinfo)" + pm_model="${pm_model##*: }" + + # load quietly and respect the blacklist + # this way if the modules are for some reason missing, it will + # still exit successfully and not affect the boot process + case "$pm_model" in + PowerMac3,6) modprobe -b -q therm_windtunnel ;; + PowerMac7,2 | PowerMac7,3) modprobe -b -q windfarm_pm72 ;; + PowerMac8,1 | PowerMac8,2) modprobe -b -q windfarm_pm81 ;; + PowerMac9,1) modprobe -b -q windfarm_pm91 ;; + PowerMac11,2) modprobe -b -q windfarm_pm112 ;; + PowerMac12,1) modprobe -b -q windfarm_pm121 ;; + RackMac3,1) modprobe -b -q windfarm_rm31 ;; + *) ;; + esac + + return 0 +} + +load_windfarm diff --git a/modules.d/90ppcmac/module-setup.sh b/modules.d/90ppcmac/module-setup.sh new file mode 100755 index 0000000..83f54de --- /dev/null +++ b/modules.d/90ppcmac/module-setup.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# +# This module attempts to properly deal with thermal behavior on PowerPC +# based Mac systems, by installing the model-appropriate (when hostonly) +# or all (when not) fan control/thermal kernel modules and loading them +# in a hook. +# +# While this is not strictly necessary for all kernels, particularly +# modular kernels will not autoload those drivers, even once the full +# system is up, which results in the fans spinning up to 100%; this is +# particularly annoying on live systems, where the system takes a while +# to load, so it's best to load the drivers early in initramfs stage. +# +# The behavior of this is inspired by the thermal hook in Debian's +# initramfs-tools, but written for dracut specifically and updated +# for modern kernels (2012+). + +# called by dracut +check() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + # only for PowerPC Macs + [[ $_arch == ppc* && $_arch != ppc64le ]] || return 1 + return 0 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + pmac_model() { + local pm_model + pm_model="$(grep model /proc/cpuinfo)" + echo "${pm_model##*: }" + } + + # only PowerMac3,6 has a module, special case + if [[ ${DRACUT_ARCH:-$(uname -m)} != ppc64* ]]; then + if ! [[ $hostonly ]] || [[ "$(pmac_model)" == "PowerMac3,6" ]]; then + instmods therm_windtunnel + fi + return 0 + fi + + windfarm_modules() { + if ! [[ $hostonly ]]; then + # include all drivers when not hostonly + instmods \ + windfarm_pm72 windfarm_pm81 windfarm_pm91 windfarm_pm112 \ + windfarm_pm121 windfarm_rm31 + else + # guess model specific module, then install the rest + case "$(pmac_model)" in + PowerMac7,2 | PowerMac7,3) instmods windfarm_pm72 ;; + PowerMac8,1 | PowerMac8,2) instmods windfarm_pm81 ;; + PowerMac9,1) instmods windfarm_pm91 ;; + PowerMac11,2) instmods windfarm_pm112 ;; + PowerMac12,1) instmods windfarm_pm121 ;; + RackMac3,1) instmods windfarm_rm31 ;; + # no match, so skip installation of the rest + *) return 1 ;; + esac + fi + return 0 + } + + # hostonly and didn't match a model; skip installing other modules + windfarm_modules || return 0 + # these are all required by the assorted windfarm_pm* + instmods \ + windfarm_core windfarm_cpufreq_clamp windfarm_pid \ + windfarm_smu_controls windfarm_smu_sat windfarm_smu_sensors \ + windfarm_fcu_controls windfarm_ad7417_sensor windfarm_max6690_sensor \ + windfarm_lm75_sensor windfarm_lm87_sensor +} + +# called by dracut +install() { + # this will attempt to load the appropriate modules + inst_hook pre-udev 99 "$moddir/load-thermal.sh" +} diff --git a/modules.d/90qemu-net/module-setup.sh b/modules.d/90qemu-net/module-setup.sh new file mode 100755 index 0000000..0fa49aa --- /dev/null +++ b/modules.d/90qemu-net/module-setup.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# called by dracut +check() { + if [[ $hostonly ]]; then + return 255 + fi + + if [[ $mount_needs ]]; then + is_qemu_virtualized && return 0 + return 255 + fi + + return 0 +} + +# called by dracut +installkernel() { + # qemu specific modules + hostonly=$(optional_hostonly) instmods virtio_net e1000 8139cp pcnet32 e100 ne2k_pci +} diff --git a/modules.d/90qemu/module-setup.sh b/modules.d/90qemu/module-setup.sh new file mode 100755 index 0000000..d11f377 --- /dev/null +++ b/modules.d/90qemu/module-setup.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# called by dracut +check() { + if [[ $hostonly ]] || [[ $mount_needs ]]; then + is_qemu_virtualized && return 0 + return 255 + fi + + return 0 +} + +# called by dracut +installkernel() { + # qemu specific modules + hostonly='' instmods \ + ata_piix ata_generic pata_acpi cdrom sr_mod ahci \ + virtio_blk virtio virtio_ring virtio_pci \ + virtio_scsi virtio_console virtio_rng virtio_mem \ + spapr-vscsi \ + qemu_fw_cfg \ + efi_secret +} diff --git a/modules.d/91crypt-gpg/README b/modules.d/91crypt-gpg/README new file mode 100644 index 0000000..be6df55 --- /dev/null +++ b/modules.d/91crypt-gpg/README @@ -0,0 +1,50 @@ +# Directions for changing a system from password-based gpg keyfile +# to smartcard-based gpg keyfile + +# Be sure that you meet the following requirements: +# 1. GnuPG >= 2.1 installed with +# * Smartcard support enabled (scdaemon must be built) +# * Direct CCID access built into scdaemon +# 2. A password-based gpg keyfile ${KEYFILE} (e.g. "keyfile.gpg"): +# That is, a file containing the slot key for LUKS, which +# has been encrypted symmetrically with GnuPG using +# a password. +# 3. Your public OpenPGP identity ${RECIPIENT} (e.g. "3A696356") +# 4. An OpenPGP smartcard holding the decryption key associated +# with your public identity +# 5. A CCID smartcard reader + +# Notes: Requirement 4. and 5. can of course be one device, e.g. +# a USB token with an integrated OpenPGP smartcard + +# Make a backup of your keyfile (assuming it lies on the boot partition) +$ cp /boot/${KEYFILE} /safe/place/keyfile.bak.gpg + +# Change your keyfile from purely password-based to both +# password-based and key-based (you can then decrypt the keyfile +# with either method). As an example aes256 is chosen, the cipher +# is not important to this guide, but do note that your kernel +# must support it at boot time (be it built into the kernel image +# or loaded as a module from the initramfs). +$ cat /safe/place/keyfile.bak.gpg | gpg -d | gpg --encrypt --recipient ${RECIPIENT} --cipher-algo aes256 --armor -c > /safe/place/keyfile_sc.gpg + +# Verify that you can decrypt your new keyfile both with the password +# and your smartcard. +# (with smartcard inserted, you should be prompted for your PIN, unless +# you already did so and have not yet timed out) +$ gpg -d /safe/place/keyfile_sc.gpg +# (with smartcard disconnected, you should be prompted for your password) +$ gpg -d /safe/place/keyfile_sc.gpg + +# After verification, replace your old keyfile with your new one +$ su -c 'cp /safe/place/keyfile_sc.gpg /boot/${KEYFILE}' + +# Export your public key to where crypt-gpg can find it +$ gpg --armor --export-options export-minimal --export ${RECIPIENT} > /safe/place/crypt-public-key.gpg +$ su -c 'cp /safe/place/crypt-public-key.gpg /etc/dracut.conf.d/crypt-public-key.gpg' + +# Rebuild your initramfs as usual +# When booting with any of the requirements not met, crypt-gpg will default to password-based keyfile unlocking. +# If all requirements are met and smartcard support is not disabled by setting the kernel option "rd.luks.smartcard=0" +# crypt-gpg will try find and use a connected OpenPGP smartcard by prompting you for the PIN and then +# unlocking the gpg keyfile with the smartcard. diff --git a/modules.d/91crypt-gpg/crypt-gpg-lib.sh b/modules.d/91crypt-gpg/crypt-gpg-lib.sh new file mode 100755 index 0000000..538419f --- /dev/null +++ b/modules.d/91crypt-gpg/crypt-gpg-lib.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +command -v ask_for_password > /dev/null || . /lib/dracut-crypt-lib.sh + +# gpg_decrypt mnt_point keypath keydev device +# +# Decrypts symmetrically encrypted (password or OpenPGP smartcard) key to standard output. +# +# mnt_point - mount point where <keydev> is already mounted +# keypath - GPG encrypted key path relative to <mnt_point> +# keydev - device on which key resides; only to display in prompt +# device - device to be opened by cryptsetup; only to display in prompt +gpg_decrypt() { + local mntp="$1" + local keypath="$2" + local keydev="$3" + local device="$4" + + local gpghome=/tmp/gnupg + local opts="--homedir $gpghome --no-mdc-warning --skip-verify --quiet" + opts="$opts --logger-file /dev/null --batch --no-tty --passphrase-fd 0" + + mkdir -m 0700 -p "$gpghome" + + # Setup GnuPG home and gpg-agent for usage of OpenPGP smartcard. + # This requires GnuPG >= 2.1, as it uses the new ,,pinentry-mode´´ + # feature, which - when set to ,,loopback´´ - allows us to pipe + # the smartcard's pin to GnuPG (instead of using a normal pinentry + # program needed with GnuPG < 2.1), making for uncomplicated + # integration with the existing codebase. + local useSmartcard="0" + local gpgMajorVersion + local gpgMinorVersion + local cmd + gpgMajorVersion="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* ([0-9]*).*|\1|p')" + gpgMinorVersion="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* [0-9]*\.([0-9]*).*|\1|p')" + + if [ "${gpgMajorVersion}" -ge 2 ] && [ "${gpgMinorVersion}" -ge 1 ] \ + && [ -f /root/crypt-public-key.gpg ] && getargbool 1 rd.luks.smartcard; then + useSmartcard="1" + echo "allow-loopback-pinentry" >> "$gpghome/gpg-agent.conf" + GNUPGHOME="$gpghome" gpg-agent --quiet --daemon + GNUPGHOME="$gpghome" gpg --quiet --no-tty --import < /root/crypt-public-key.gpg + local smartcardSerialNumber + smartcardSerialNumber="$(GNUPGHOME=$gpghome gpg --no-tty --card-status \ + | sed -n -r -e 's|Serial number.*: ([0-9]*)|\1|p' | tr -d '\n')" + if [ -n "${smartcardSerialNumber}" ]; then + inputPrompt="PIN (OpenPGP card ${smartcardSerialNumber})" + fi + GNUPGHOME="$gpghome" gpg-connect-agent 1> /dev/null learn /bye + opts="$opts --pinentry-mode=loopback" + cmd="GNUPGHOME=$gpghome gpg --card-status --no-tty > /dev/null 2>&1; gpg $opts --decrypt $mntp/$keypath" + else + cmd="gpg $opts --decrypt $mntp/$keypath" + fi + + ask_for_password \ + --cmd "$cmd" \ + --prompt "${inputPrompt:-Password ($keypath on $keydev for $device)}" \ + --tries 3 --tty-echo-off + + # Clean up the smartcard gpg-agent + if [ "${useSmartcard}" = "1" ]; then + GNUPGHOME="$gpghome" gpg-connect-agent 1> /dev/null killagent /bye + fi + + rm -rf -- "$gpghome" +} diff --git a/modules.d/91crypt-gpg/module-setup.sh b/modules.d/91crypt-gpg/module-setup.sh new file mode 100755 index 0000000..501869a --- /dev/null +++ b/modules.d/91crypt-gpg/module-setup.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# GPG support is optional +# called by dracut +check() { + require_binaries gpg tr || return 1 + + if sc_requested; then + if ! sc_supported; then + dwarning "crypt-gpg: GnuPG >= 2.1 with scdaemon and libusb required for ccid smartcard support" + return 1 + fi + return 0 + fi + + return 255 +} + +# called by dracut +depends() { + echo crypt +} + +# called by dracut +install() { + inst_multiple gpg tr + inst "$moddir/crypt-gpg-lib.sh" "/lib/dracut-crypt-gpg-lib.sh" + + if sc_requested; then + inst_multiple gpg-agent + inst_multiple gpg-connect-agent + inst_multiple -o /usr/libexec/scdaemon /usr/lib/gnupg/scdaemon + cp "$dracutsysrootdir$(sc_public_key)" "${initdir}/root/" + fi +} + +sc_public_key() { + echo -n "/etc/dracut.conf.d/crypt-public-key.gpg" +} + +# CCID Smartcard support requires GnuPG >= 2.1 with scdaemon and libusb +sc_supported() { + local gpgMajor + local gpgMinor + local scdaemon + gpgMajor="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* ([0-9]*).*|\1|p')" + gpgMinor="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* [0-9]*\.([0-9]*).*|\1|p')" + + if [[ -x "$dracutsysrootdir"/usr/libexec/scdaemon ]]; then + scdaemon=/usr/libexec/scdaemon + elif [[ -x "$dracutsysrootdir"/usr/lib/gnupg/scdaemon ]]; then + scdaemon=/usr/lib/gnupg/scdaemon + else + return 1 + fi + + if [[ ${gpgMajor} -gt 2 || ${gpgMajor} -eq 2 && ${gpgMinor} -ge 1 ]] \ + && require_binaries gpg-agent \ + && require_binaries gpg-connect-agent \ + && ($DRACUT_LDD "${dracutsysrootdir}${scdaemon}" | grep libusb > /dev/null); then + return 0 + else + return 1 + fi +} + +sc_requested() { + if [ -f "$dracutsysrootdir$(sc_public_key)" ]; then + return 0 + else + return 1 + fi +} diff --git a/modules.d/91crypt-loop/crypt-loop-lib.sh b/modules.d/91crypt-loop/crypt-loop-lib.sh new file mode 100755 index 0000000..7db82e2 --- /dev/null +++ b/modules.d/91crypt-loop/crypt-loop-lib.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +command -v ask_for_password > /dev/null || . /lib/dracut-crypt-lib.sh + +# loop_decrypt mnt_point keypath keydev device +# +# Decrypts symmetrically encrypted key to standard output. +# +# mnt_point - mount point where <keydev> is already mounted +# keypath - LUKS encrypted loop file path relative to <mnt_point> +# keydev - device on which key resides; only to display in prompt +# device - device to be opened by cryptsetup; only to display in prompt +loop_decrypt() { + local mntp="$1" + local keypath="$2" + local keydev="$3" + local device="$4" + local key + + key="/dev/mapper/$(str_replace "loop-$keydev-$mntp-$keypath" '/' '-')" + + if [ ! -b "$key" ]; then + local loopdev + local opts + loopdev=$(losetup -f "${mntp}/${keypath}" --show) + opts="-d - luksOpen $loopdev ${key##*/}" + + ask_for_password \ + --cmd "cryptsetup $opts" \ + --prompt "Password ($keypath on $keydev for $device)" \ + --tty-echo-off + + [ -b "$key" ] || die "Failed to unlock $keypath on $keydev for $device." + + printf "%s\n" "cryptsetup luksClose \"$key\"" > "${hookdir}/cleanup/crypt-loop-cleanup-10-${key##*/}.sh" + printf "%s\n" "losetup -d \"$loopdev\"" > "${hookdir}/cleanup/crypt-loop-cleanup-20-${loopdev##*/}.sh" + fi + + cat "$key" +} diff --git a/modules.d/91crypt-loop/module-setup.sh b/modules.d/91crypt-loop/module-setup.sh new file mode 100755 index 0000000..ff0d501 --- /dev/null +++ b/modules.d/91crypt-loop/module-setup.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# called by dracut +check() { + require_binaries losetup || return 1 + + return 255 +} + +# called by dracut +depends() { + echo crypt +} + +# called by dracut +installkernel() { + hostonly='' instmods loop +} + +# called by dracut +install() { + inst_multiple losetup + inst "$moddir/crypt-loop-lib.sh" "/lib/dracut-crypt-loop-lib.sh" + dracut_need_initqueue +} diff --git a/modules.d/91fido2/module-setup.sh b/modules.d/91fido2/module-setup.sh new file mode 100755 index 0000000..def7a0f --- /dev/null +++ b/modules.d/91fido2/module-setup.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + # Return 255 to only include the module, if another module requires it. + return 255 +} + +# Module dependency requirements. +depends() { + # This module has external dependency on other module(s). + echo systemd-udevd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + # Install required libraries. + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file \ + {"tls/$_arch/",tls/,"$_arch/",}"libfido2.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libz.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libcryptsetup.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"/cryptsetup/libcryptsetup-token-systemd-fido2.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"libcbor.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libhidapi-hidraw.so.*" +} diff --git a/modules.d/91pcsc/module-setup.sh b/modules.d/91pcsc/module-setup.sh new file mode 100755 index 0000000..6f8b2c8 --- /dev/null +++ b/modules.d/91pcsc/module-setup.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries pcscd || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd-udevd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + inst_simple "$moddir/pcscd.service" "${systemdsystemunitdir}"/pcscd.service + inst_simple "$moddir/pcscd.socket" "${systemdsystemunitdir}"/pcscd.socket + + inst_multiple -o \ + pcscd + + # Enable systemd type unit(s) + for i in \ + pcscd.service \ + pcscd.socket; do + $SYSTEMCTL -q --root "$initdir" enable "$i" + done + + # Install library file(s) + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file \ + {"tls/$_arch/",tls/,"$_arch/",}"libopensc.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libsmm-local.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"opensc-pkcs11.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"onepin-opensc-pkcs11.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"pkcs11/opensc-pkcs11.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"pkcs11/onepin-opensc-pkcs11.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist" \ + {"tls/$_arch/",tls/,"$_arch/",}"pcsc/drivers/ifd-ccid.bundle/Contents/Linux/libccid.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"pcsc/drivers/serial/libccidtwin.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"libpcsclite.so.*" + + # Install the hosts local user configurations if enabled. + if [[ $hostonly ]]; then + inst_multiple -H -o \ + /etc/opensc.conf \ + "/etc/reader.conf.d/*" + fi + +} diff --git a/modules.d/91pcsc/pcscd.service b/modules.d/91pcsc/pcscd.service new file mode 100644 index 0000000..639decd --- /dev/null +++ b/modules.d/91pcsc/pcscd.service @@ -0,0 +1,13 @@ +[Unit] +DefaultDependencies=no +Description=PC/SC Smart Card Daemon (Dracut) +Documentation=man:pcscd(8) +Requires=pcscd.socket + +[Service] +ExecStart=/usr/sbin/pcscd --foreground --auto-exit +ExecReload=/usr/sbin/pcscd --hotplug + +[Install] +Also=pcscd.socket +WantedBy=cryptsetup-pre.target diff --git a/modules.d/91pcsc/pcscd.socket b/modules.d/91pcsc/pcscd.socket new file mode 100644 index 0000000..b20dd5a --- /dev/null +++ b/modules.d/91pcsc/pcscd.socket @@ -0,0 +1,11 @@ +[Unit] +DefaultDependencies=no +Description=PC/SC Smart Card Daemon Activation Socket (Dracut) +Documentation=man:pcscd(8) + +[Socket] +ListenStream=/run/pcscd/pcscd.comm +SocketMode=0666 + +[Install] +WantedBy=cryptsetup-pre.target sockets.target diff --git a/modules.d/91pkcs11/module-setup.sh b/modules.d/91pkcs11/module-setup.sh new file mode 100755 index 0000000..5675efb --- /dev/null +++ b/modules.d/91pkcs11/module-setup.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd-udevd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + # Install library file(s) + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file \ + {"tls/$_arch/",tls/,"$_arch/",}"libtasn1.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libffi.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libp11-kit.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libcryptsetup.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"/cryptsetup/libcryptsetup-token-systemd-pkcs11.so*" + +} diff --git a/modules.d/91tpm2-tss/module-setup.sh b/modules.d/91tpm2-tss/module-setup.sh new file mode 100755 index 0000000..e145c41 --- /dev/null +++ b/modules.d/91tpm2-tss/module-setup.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# This file is part of dracut. +# SPDX-License-Identifier: GPL-2.0-or-later + +# Prerequisite check(s) for module. +check() { + + # If the binary(s) requirements are not fulfilled the module can't be installed. + require_binaries tpm2 || return 1 + + # Return 255 to only include the module, if another module requires it. + return 255 + +} + +# Module dependency requirements. +depends() { + + # This module has external dependency on other module(s). + echo systemd-sysusers systemd-udevd + # Return 0 to include the dependent module(s) in the initramfs. + return 0 + +} + +# Install kernel module(s). +installkernel() { + instmods '=drivers/char/tpm' +} + +# Install the required file(s) and directories for the module in the initramfs. +install() { + + inst_multiple -o \ + "$sysusers"/tpm2-tss.conf \ + "$tmpfilesdir"/tpm2-tss-fapi.conf \ + "$udevrulesdir"/60-tpm-udev.rules \ + tpm2_pcrread tpm2_pcrextend tpm2_createprimary tpm2_createpolicy \ + tpm2_create tpm2_load tpm2_unseal tpm2 + + # Install library file(s) + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-esys.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-fapi.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-mu.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-rc.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-sys.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-tcti-cmd.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-tcti-device.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-tcti-mssim.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-tcti-swtpm.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libtss2-tctildr.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libcryptsetup.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"/cryptsetup/libcryptsetup-token-systemd-tpm2.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"libcurl.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libjson-c.so.*" + +} diff --git a/modules.d/91zipl/install_zipl_cmdline.sh b/modules.d/91zipl/install_zipl_cmdline.sh new file mode 100755 index 0000000..9332d31 --- /dev/null +++ b/modules.d/91zipl/install_zipl_cmdline.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +DEV="$1" +MNT=/boot/zipl + +if [ -z "$DEV" ]; then + echo "No IPL device given" + : > /tmp/install.zipl.cmdline-done + exit 1 +fi + +[ -d ${MNT} ] || mkdir -p ${MNT} + +if ! mount -o ro "${DEV}" ${MNT}; then + echo "Failed to mount ${MNT}" + : > /tmp/install.zipl.cmdline-done + exit 1 +fi + +if [ -f ${MNT}/dracut-cmdline.conf ]; then + cp ${MNT}/dracut-cmdline.conf /etc/cmdline.d/99zipl.conf +fi + +if [ -f ${MNT}/active_devices.txt ]; then + while read -r dev _ || [[ $dev ]]; do + [ "$dev" = "#" -o "$dev" = "" ] && continue + cio_ignore -r "$dev" + done < ${MNT}/active_devices.txt +fi + +umount ${MNT} + +if [ -f /etc/cmdline.d/99zipl.conf ]; then + systemctl restart dracut-cmdline.service + systemctl restart systemd-udev-trigger.service +fi +: > /tmp/install.zipl.cmdline-done + +exit 0 diff --git a/modules.d/91zipl/module-setup.sh b/modules.d/91zipl/module-setup.sh new file mode 100755 index 0000000..cb21454 --- /dev/null +++ b/modules.d/91zipl/module-setup.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +get_boot_zipl_dev() { + local _boot_zipl + _boot_zipl=$(sed -n -e '/^[[:space:]]*#/d' -e 's/\(.*\)\w*\/boot\/zipl.*/\1/p' "$dracutsysrootdir"/etc/fstab) + printf "%s" "$(trim "$_boot_zipl")" +} + +# called by dracut +check() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + # Only for systems on s390 using indirect booting via userland grub + [ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1 + # /boot/zipl contains a first stage kernel used to launch grub in initrd + [ -d /boot/zipl ] || return 1 + return 0 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + local _boot_zipl + + _boot_zipl=$(get_boot_zipl_dev) + if [ -n "$_boot_zipl" ]; then + eval "$(blkid -s TYPE -o udev "${_boot_zipl}")" + if [ -n "$ID_FS_TYPE" ]; then + case "$ID_FS_TYPE" in + ext?) + ID_FS_TYPE=ext4 + ;; + esac + instmods ${ID_FS_TYPE} + fi + fi +} + +# called by dracut +cmdline() { + local _boot_zipl + + _boot_zipl=$(get_boot_zipl_dev) + if [ -n "$_boot_zipl" ]; then + printf "%s" " rd.zipl=${_boot_zipl}" + fi +} + +# called by dracut +install() { + inst_multiple mount umount + + inst_hook cmdline 91 "$moddir/parse-zipl.sh" + inst_script "${moddir}/install_zipl_cmdline.sh" /sbin/install_zipl_cmdline.sh + if [[ $hostonly_cmdline == "yes" ]]; then + local _zipl + _zipl=$(cmdline) + + [[ $_zipl ]] && printf "%s\n" "$_zipl" > "${initdir}/etc/cmdline.d/91zipl.conf" + fi + dracut_need_initqueue +} diff --git a/modules.d/91zipl/parse-zipl.sh b/modules.d/91zipl/parse-zipl.sh new file mode 100755 index 0000000..d95a1dd --- /dev/null +++ b/modules.d/91zipl/parse-zipl.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +zipl_arg=$(getarg rd.zipl) + +if [ -n "$zipl_arg" ]; then + case "$zipl_arg" in + LABEL=*) + zipl_env="ENV{ID_FS_LABEL}" + zipl_val=${zipl_arg#LABEL=} + zipl_arg="$(label_uuid_to_dev "${zipl_val}")" + ;; + UUID=*) + zipl_env="ENV{ID_FS_UUID}" + zipl_val=${zipl_arg#UUID=} + zipl_arg="$(label_uuid_to_dev "${zipl_val}")" + ;; + PARTLABEL=*) + zipl_env="ENV{ID_FS_PARTLABEL}" + zipl_val=${zipl_arg#PARTLABEL=} + zipl_arg="$(label_uuid_to_dev "${zipl_val}")" + ;; + PARTUUID=*) + zipl_env="ENV{ID_FS_PARTUUID}" + zipl_val=${zipl_arg#PARTUUID=} + zipl_arg="$(label_uuid_to_dev "${zipl_val}")" + ;; + /dev/mapper/*) + zipl_env="ENV{DM_NAME}" + zipl_val=${zipl_arg#/dev/mapper/} + ;; + /dev/disk/by-*) + zipl_env="SYMLINK" + zipl_val=${zipl_arg#/dev/} + ;; + /dev/*) + zipl_env="KERNEL" + zipl_val=${zipl_arg} + ;; + esac + if [ "$zipl_env" ]; then + { + printf 'ACTION=="add|change", SUBSYSTEM=="block", %s=="%s", ENV{SYSTEMD_READY}!="0", RUN+="/sbin/initqueue --settled --onetime --unique --name install_zipl_cmdline /sbin/install_zipl_cmdline.sh %s"\n' \ + ${zipl_env} "${zipl_val}" "${zipl_arg}" + echo "[ -f /tmp/install.zipl.cmdline-done ]" > "$hookdir"/initqueue/finished/wait-zipl-conf.sh + } >> /etc/udev/rules.d/99zipl-conf.rules + cat /etc/udev/rules.d/99zipl-conf.rules + fi + wait_for_dev -n "$zipl_arg" +fi diff --git a/modules.d/95cifs/cifs-lib.sh b/modules.d/95cifs/cifs-lib.sh new file mode 100755 index 0000000..b996b41 --- /dev/null +++ b/modules.d/95cifs/cifs-lib.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# cifs_to_var CIFSROOT +# use CIFSROOT to set $server, $path, and $options. +# CIFSROOT is something like: cifs://[<username>[:<password>]]@<host>/<path> +# NETIF is used to get information from DHCP options, if needed. + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +cifs_to_var() { + local cifsuser + local cifspass + # Check required arguments + server=${1##cifs://} + cifsuser=${server%@*} + cifspass=${cifsuser#*:} + if [ "$cifspass" != "$cifsuser" ]; then + cifsuser=${cifsuser%:*} + else + cifspass=$(getarg cifspass) + fi + if [ "$cifsuser" != "$server" ]; then + server="${server#*@}" + else + cifsuser=$(getarg cifsuser) + fi + + # shellcheck disable=SC2034 + path=${server#*/} + # shellcheck disable=SC2034 + server=${server%/*} + + if [ ! "$cifsuser" -o ! "$cifspass" ]; then + die "For CIFS support you need to specify a cifsuser and cifspass either in the cifsuser and cifspass commandline parameters or in the root= CIFS URL." + fi + # shellcheck disable=SC2034 + options="user=$cifsuser,pass=$cifspass,$(getarg rootflags=)" +} diff --git a/modules.d/95cifs/cifsroot.sh b/modules.d/95cifs/cifsroot.sh new file mode 100755 index 0000000..83539d4 --- /dev/null +++ b/modules.d/95cifs/cifsroot.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +. /lib/cifs-lib.sh + +[ "$#" = 3 ] || exit 1 + +# root is in the form root=cifs://user:pass@[server]/[folder] either from +# cmdline or dhcp root-path +#netif="$1" +root="$2" +NEWROOT="$3" + +cifs_to_var "$root" + +mount.cifs "//$server/$path" "$NEWROOT" -o "$options" && { [ -e /dev/root ] || ln -s null /dev/root; } + +# inject new exit_if_exists +# shellcheck disable=SC2016 +echo 'settle_exit_if_exists="--exit-if-exists=/dev/root"; rm -f -- "$job"' > "$hookdir"/initqueue/cifs.sh +# force udevsettle to break +: > "$hookdir"/initqueue/work diff --git a/modules.d/95cifs/module-setup.sh b/modules.d/95cifs/module-setup.sh new file mode 100755 index 0000000..6abc475 --- /dev/null +++ b/modules.d/95cifs/module-setup.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# called by dracut +check() { + # If our prerequisites are not met, fail anyways. + require_binaries mount.cifs || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for fs in "${host_fs_types[@]}"; do + [[ $fs == "cifs" ]] && return 0 + done + return 255 + } + + return 0 +} + +# called by dracut +depends() { + # We depend on network modules being loaded + echo network +} + +# called by dracut +installkernel() { + instmods cifs ipv6 + # hash algos + instmods md4 md5 sha256 sha512 + # ciphers + instmods aes arc4 des ecb gcm aead2 + # macs + instmods hmac cmac ccm +} + +# called by dracut +install() { + local _nsslibs + inst_multiple -o mount.cifs + inst_multiple -o /etc/services /etc/nsswitch.conf /etc/protocols + inst_multiple -o /usr/etc/services /usr/etc/nsswitch.conf /usr/etc/protocols + + inst_libdir_file 'libcap-ng.so*' + + _nsslibs=$( + cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \ + | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \ + | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|' + ) + _nsslibs=${_nsslibs#|} + _nsslibs=${_nsslibs%|} + + inst_libdir_file -n "$_nsslibs" 'libnss_*.so*' + + inst_hook cmdline 90 "$moddir/parse-cifsroot.sh" + inst "$moddir/cifsroot.sh" "/sbin/cifsroot" + inst "$moddir/cifs-lib.sh" "/lib/cifs-lib.sh" + dracut_need_initqueue +} diff --git a/modules.d/95cifs/parse-cifsroot.sh b/modules.d/95cifs/parse-cifsroot.sh new file mode 100755 index 0000000..b88bef4 --- /dev/null +++ b/modules.d/95cifs/parse-cifsroot.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# +# root=cifs://[user:pass@]<server>/<folder> +# +# This syntax can come from DHCP root-path as well. +# +# If a username or password are not specified as part of the root, then they +# will be pulled from cifsuser and cifspass on the kernel command line, +# respectively. +# + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +. /lib/cifs-lib.sh + +# This script is sourced, so root should be set. But let's be paranoid +[ -z "$root" ] && root=$(getarg root=) + +if [ -z "$netroot" ]; then + for netroot in $(getargs netroot=); do + [ "${netroot%%:*}" = "cifs" ] && break + done + [ "${netroot%%:*}" = "cifs" ] || unset netroot +fi + +# Root takes precedence over netroot +if [ "${root%%:*}" = "cifs" ]; then + if [ -n "$netroot" ]; then + warn "root takes precedence over netroot. Ignoring netroot" + fi + netroot=$root + unset root +fi + +# If it's not cifs we don't continue +[ "${netroot%%:*}" = "cifs" ] || return + +# Check required arguments +cifs_to_var "$netroot" + +# If we don't have a server, we need dhcp +if [ -z "$server" ]; then + # shellcheck disable=SC2034 + DHCPORSERVER="1" +fi + +# Done, all good! +# shellcheck disable=SC2034 +rootok=1 + +# shellcheck disable=SC2016 +echo '[ -e $NEWROOT/proc ]' > "$hookdir"/initqueue/finished/cifsroot.sh diff --git a/modules.d/95dasd/module-setup.sh b/modules.d/95dasd/module-setup.sh new file mode 100755 index 0000000..180da14 --- /dev/null +++ b/modules.d/95dasd/module-setup.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# called by dracut +check() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + [ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1 + require_binaries normalize_dasd_arg || return 1 + return 0 +} + +# called by dracut +depends() { + echo "dasd_mod" + return 0 +} + +# called by dracut +install() { + inst_hook cmdline 30 "$moddir/parse-dasd.sh" + inst_multiple dasdinfo dasdconf.sh normalize_dasd_arg + conf=/etc/dasd.conf + if [[ $hostonly && -f $conf ]]; then + inst -H $conf + fi + inst_rules 56-dasd.rules + inst_rules 59-dasd.rules +} diff --git a/modules.d/95dasd/parse-dasd.sh b/modules.d/95dasd/parse-dasd.sh new file mode 100755 index 0000000..cda3970 --- /dev/null +++ b/modules.d/95dasd/parse-dasd.sh @@ -0,0 +1,11 @@ +#!/bin/sh +for dasd_arg in $(getargs rd.dasd= -d rd_DASD= DASD=); do + ( + local OLDIFS="$IFS" + IFS="," + # shellcheck disable=SC2086 + set -- $dasd_arg + IFS="$OLDIFS" + echo "$@" | normalize_dasd_arg >> /etc/dasd.conf + ) +done diff --git a/modules.d/95dasd_mod/module-setup.sh b/modules.d/95dasd_mod/module-setup.sh new file mode 100755 index 0000000..c59dd3a --- /dev/null +++ b/modules.d/95dasd_mod/module-setup.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# called by dracut +check() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + [ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1 + require_binaries grep sed seq + + return 0 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + instmods dasd_mod dasd_eckd_mod dasd_fba_mod dasd_diag_mod +} + +# called by dracut +install() { + inst_hook cmdline 31 "$moddir/parse-dasd-mod.sh" + inst_multiple grep sed seq + inst_multiple -o dasd_cio_free +} diff --git a/modules.d/95dasd_mod/parse-dasd-mod.sh b/modules.d/95dasd_mod/parse-dasd-mod.sh new file mode 100755 index 0000000..2b86d45 --- /dev/null +++ b/modules.d/95dasd_mod/parse-dasd-mod.sh @@ -0,0 +1,18 @@ +#!/bin/sh +mod_args="" + +for dasd_arg in $(getargs rd.dasd= -d rd_DASD= DASD=); do + mod_args="$mod_args,$dasd_arg" +done + +mod_args="${mod_args#*,}" + +if [ -x /sbin/dasd_cio_free -a -n "$mod_args" ]; then + [ -d /etc/modprobe.d ] || mkdir -m 0755 -p /etc/modprobe.d + echo "options dasd_mod dasd=$mod_args" >> /etc/modprobe.d/dasd_mod.conf +fi + +unset dasd_arg +if [ -x /sbin/dasd_cio_free ]; then + dasd_cio_free +fi diff --git a/modules.d/95dasd_rules/module-setup.sh b/modules.d/95dasd_rules/module-setup.sh new file mode 100755 index 0000000..06c57a4 --- /dev/null +++ b/modules.d/95dasd_rules/module-setup.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# called by dracut +cmdline() { + is_dasd() { + local _dev=$1 + local _devpath + _devpath=$( + cd -P /sys/dev/block/"$_dev" || exit + echo "$PWD" + ) + + [ "${_devpath#*/dasd}" == "$_devpath" ] && return 1 + _ccw="${_devpath%%/block/*}" + echo "rd.dasd=${_ccw##*/}" + return 0 + } + [[ $hostonly ]] || [[ $mount_needs ]] && { + for_each_host_dev_and_slaves_all is_dasd || return 255 + } | sort | uniq +} + +# called by dracut +check() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + local found=0 + local bdev + [ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for bdev in /sys/block/*; do + case "${bdev##*/}" in + dasd*) + found=$((found + 1)) + break + ;; + esac + done + [ $found -eq 0 ] && return 255 + } + return 0 +} + +# called by dracut +depends() { + echo 'dasd_mod' bash + return 0 +} + +# called by dracut +install() { + inst_hook cmdline 30 "$moddir/parse-dasd.sh" + if [[ $hostonly_cmdline == "yes" ]]; then + local _dasd + _dasd=$(cmdline) + [[ $_dasd ]] && printf "%s\n" "$_dasd" >> "${initdir}/etc/cmdline.d/95dasd.conf" + fi + if [[ $hostonly ]]; then + inst_rules_wildcard "51-dasd-*.rules" + inst_rules_wildcard "41-dasd-*.rules" + mark_hostonly /etc/udev/rules.d/51-dasd-*.rules + mark_hostonly /etc/udev/rules.d/41-dasd-*.rules + fi + inst_rules 59-dasd.rules +} diff --git a/modules.d/95dasd_rules/parse-dasd.sh b/modules.d/95dasd_rules/parse-dasd.sh new file mode 100755 index 0000000..4454aec --- /dev/null +++ b/modules.d/95dasd_rules/parse-dasd.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +allow_device() { + local ccw=$1 + + if [ -x /sbin/cio_ignore ] && cio_ignore -i "$ccw" > /dev/null; then + cio_ignore -r "$ccw" + fi +} + +if [[ -f /sys/firmware/ipl/ipl_type ]] && [[ $(< /sys/firmware/ipl/ipl_type) == "ccw" ]]; then + allow_device "$(< /sys/firmware/ipl/device)" +fi + +for dasd_arg in $(getargs root=) $(getargs resume=); do + [[ $dasd_arg =~ /dev/disk/by-path/ccw-* ]] || continue + + ccw_dev="${dasd_arg##*/ccw-}" + allow_device "${ccw_dev%%-*}" +done + +for dasd_arg in $(getargs rd.dasd=); do + IFS=',' read -r -a devs <<< "$dasd_arg" + declare -p devs + for dev in "${devs[@]}"; do + case "$dev" in + autodetect | probeonly) ;; + + *-*) + IFS="-" read -r start end _ <<< "${dev%(ro)}" + prefix=${start%.*} + start=${start##*.} + for rdev in $(seq $((16#$start)) $((16#$end))); do + allow_device "$(printf "%s.%04x" "$prefix" "$rdev")" + done + ;; + *) + IFS="." read -r sid ssid chan _ <<< "${dev%(ro)}" + allow_device "$(printf "%01x.%01x.%04x" $((16#$sid)) $((16#$ssid)) $((16#$chan)))" + ;; + esac + done +done diff --git a/modules.d/95dcssblk/module-setup.sh b/modules.d/95dcssblk/module-setup.sh new file mode 100755 index 0000000..67af4ad --- /dev/null +++ b/modules.d/95dcssblk/module-setup.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +# called by dracut +check() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + [[ $_arch == "s390" ]] || [[ $_arch == "s390x" ]] || return 1 + return 0 +} + +# called by dracut +installkernel() { + if [[ $hostonly ]]; then + for dev in /sys/devices/dcssblk/*/block/dcssblk*; do + [[ -e $dev ]] || continue + hostonly='' instmods dcssblk + return $? + done + else + hostonly='' instmods dcssblk + fi +} + +# called by dracut +install() { + inst_hook cmdline 30 "$moddir/parse-dcssblk.sh" + # If there is a config file which contains avail (best only of root device) + # disks to activate add it and use it during boot -> then we do not need + # a kernel param anymore + #if [[ $hostonly ]]; then + # inst /etc/dcssblk.conf + #fi +} diff --git a/modules.d/95dcssblk/parse-dcssblk.sh b/modules.d/95dcssblk/parse-dcssblk.sh new file mode 100755 index 0000000..980160a --- /dev/null +++ b/modules.d/95dcssblk/parse-dcssblk.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +if dcssblk_arg=$(getarg rd.dcssblk=); then + info "Loading dcssblk segments=$dcssblk_arg" + modprobe dcssblk segments="$dcssblk_arg" +fi diff --git a/modules.d/95debug/module-setup.sh b/modules.d/95debug/module-setup.sh new file mode 100755 index 0000000..3124f1a --- /dev/null +++ b/modules.d/95debug/module-setup.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# called by dracut +check() { + # do not add this module by default + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_multiple -o ls ps grep more cat rm strace free showmount df du lsblk \ + ping netstat rpcinfo vi scp ping6 ssh find chroot \ + tcpdump cp dd less hostname mkdir systemd-analyze \ + fsck fsck.ext2 fsck.ext4 fsck.ext3 fsck.ext4dev fsck.f2fs fsck.vfat e2fsck + + grep '^tcpdump:' "$dracutsysrootdir"/etc/passwd 2> /dev/null >> "$initdir/etc/passwd" +} diff --git a/modules.d/95fcoe-uefi/module-setup.sh b/modules.d/95fcoe-uefi/module-setup.sh new file mode 100755 index 0000000..9dc1e73 --- /dev/null +++ b/modules.d/95fcoe-uefi/module-setup.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# called by dracut +check() { + is_fcoe() { + block_is_fcoe "$1" || return 1 + } + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for_each_host_dev_and_slaves is_fcoe || return 255 + [ -d /sys/firmware/efi ] || return 255 + } + + require_binaries dcbtool fipvlan lldpad ip readlink || return 1 + return 0 +} + +# called by dracut +depends() { + echo fcoe uefi-lib bash + return 0 +} + +# called by dracut +install() { + inst_hook cmdline 20 "$moddir/parse-uefifcoe.sh" +} diff --git a/modules.d/95fcoe-uefi/parse-uefifcoe.sh b/modules.d/95fcoe-uefi/parse-uefifcoe.sh new file mode 100755 index 0000000..e120dec --- /dev/null +++ b/modules.d/95fcoe-uefi/parse-uefifcoe.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +command -v getarg > /dev/null || . /lib/dracut-lib.sh +command -v get_fcoe_boot_mac > /dev/null || . /lib/uefi-lib.sh +command -v set_ifname > /dev/null || . /lib/net-lib.sh + +print_fcoe_uefi_conf() { + local mac dev vlan + mac=$(get_fcoe_boot_mac "$1") + [ -z "$mac" ] && return 1 + dev=$(set_ifname fcoe "$mac") + vlan=$(get_fcoe_boot_vlan "$1") + if [ "$vlan" -ne "0" ]; then + case "$vlan" in + [0-9]*) + printf "%s\n" "vlan=$dev.$vlan:$dev" + dev="$dev.$vlan" + ;; + *) + printf "%s\n" "vlan=$vlan:$dev" + dev="$vlan" + ;; + esac + fi + # fcoe=eth0:nodcb + printf "fcoe=%s\n" "$dev:nodcb" + return 0 +} + +for i in /sys/firmware/efi/efivars/FcoeBootDevice-*; do + [ -e "$i" ] || continue + print_fcoe_uefi_conf "$i" > /etc/cmdline.d/40-fcoe-uefi.conf && break +done diff --git a/modules.d/95fcoe/cleanup-fcoe.sh b/modules.d/95fcoe/cleanup-fcoe.sh new file mode 100755 index 0000000..210fb94 --- /dev/null +++ b/modules.d/95fcoe/cleanup-fcoe.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +if [ -e /var/run/lldpad.pid ]; then + lldpad -k + # with systemd version 230, this is not necessary anymore + # systemd commit cacf980ed44a28e276a6cc7f8fc41f991e2ab354 + if [ -z "$DRACUT_SYSTEMD" ]; then + # shellcheck disable=SC2174 + mkdir -m 0755 -p /run/initramfs/state/dev/shm + cp /dev/shm/lldpad.state /run/initramfs/state/dev/shm/ > /dev/null 2>&1 + echo "files /dev/shm/lldpad.state" >> /run/initramfs/rwtab + fi +fi diff --git a/modules.d/95fcoe/fcoe-edd.sh b/modules.d/95fcoe/fcoe-edd.sh new file mode 100755 index 0000000..17f8a7c --- /dev/null +++ b/modules.d/95fcoe/fcoe-edd.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +dcb="$1" + +_modprobe_r_edd="0" + +check_edd() { + local cnt=0 + + [ -d /sys/firmware/edd ] && return 0 + + _modprobe_r_edd="1" + modprobe edd || return $? + + while [ $cnt -lt 600 ]; do + [ -d /sys/firmware/edd ] && return 0 + cnt=$((cnt + 1)) + sleep 0.1 + done + return 1 +} + +check_edd || exit 1 + +for disk in /sys/firmware/edd/int13_*; do + [ -d "$disk" ] || continue + if [ -e "${disk}/pci_dev/driver" ]; then + driver=$(readlink "${disk}/pci_dev/driver") + driver=${driver##*/} + fi + # i40e uses dev_port 1 for a virtual fcoe function + if [ "${driver}" = "i40e" ]; then + dev_port=1 + fi + for nic in "${disk}"/pci_dev/net/*; do + [ -d "$nic" ] || continue + if [ -n "${dev_port}" -a -e "${nic}/dev_port" ]; then + if [ "$(cat "${nic}"/dev_port)" -ne "${dev_port}" ]; then + continue + fi + fi + if [ -e "${nic}"/address ]; then + fcoe_interface=${nic##*/} + if ! [ -e "/tmp/.fcoe-$fcoe_interface" ]; then + /sbin/fcoe-up "$fcoe_interface" "$dcb" + : > "/tmp/.fcoe-$fcoe_interface" + fi + fi + done +done + +[ "$_modprobe_r_edd" = "1" ] && modprobe -r edd + +unset _modprobe_r_edd diff --git a/modules.d/95fcoe/fcoe-up.sh b/modules.d/95fcoe/fcoe-up.sh new file mode 100755 index 0000000..0828f03 --- /dev/null +++ b/modules.d/95fcoe/fcoe-up.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# We get called like this: +# fcoe-up <network-device> <dcb|nodcb> <fabric|vn2vn> +# +# Note currently only nodcb is supported, the dcb option is reserved for +# future use. + +PATH=/usr/sbin:/usr/bin:/sbin:/bin +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +type ip_to_var > /dev/null 2>&1 || . /lib/net-lib.sh + +# Huh? Missing arguments ?? +[ -z "$1" -o -z "$2" ] && exit 1 + +netif=$1 +dcb=$2 +mode=$3 +vlan="yes" + +read -r iflink < /sys/class/net/"$netif"/iflink +read -r ifindex < /sys/class/net/"$netif"/ifindex +if [ "$iflink" != "$ifindex" ]; then + # Skip VLAN devices + exit 0 +fi + +ip link set dev "$netif" up +linkup "$netif" + +# Some fcoemon implementations expect --syslog=true +syslogopt="--syslog" +if fcoemon -h | grep syslog | grep -q yes; then + syslogopt="$syslogopt=yes" +fi + +netdriver=$(readlink -f /sys/class/net/"$netif"/device/driver) +netdriver=${netdriver##*/} + +write_fcoemon_cfg() { + [ -f /etc/fcoe/cfg-"$netif" ] && return + echo FCOE_ENABLE=\"yes\" > /etc/fcoe/cfg-"$netif" + if [ "$dcb" = "dcb" ]; then + echo DCB_REQUIRED=\"yes\" >> /etc/fcoe/cfg-"$netif" + else + echo DCB_REQUIRED=\"no\" >> /etc/fcoe/cfg-"$netif" + fi + if [ "$vlan" = "yes" ]; then + echo AUTO_VLAN=\"yes\" >> /etc/fcoe/cfg-"$netif" + else + echo AUTO_VLAN=\"no\" >> /etc/fcoe/cfg-"$netif" + fi + if [ "$mode" = "vn2vn" ]; then + echo MODE=\"vn2vn\" >> /etc/fcoe/cfg-"$netif" + else + echo MODE=\"fabric\" >> /etc/fcoe/cfg-"$netif" + fi +} + +if [ "$netdriver" = "bnx2x" ]; then + # If driver is bnx2x, do not use /sys/module/fcoe/parameters/create but fipvlan + modprobe 8021q + udevadm settle --timeout=30 + # Sleep for 13 s to allow dcb negotiation + sleep 13 + fipvlan "$netif" -c -s + need_shutdown + exit +fi +if [ "$dcb" = "dcb" ]; then + # wait for lldpad to be ready + i=0 + while [ $i -lt 60 ]; do + lldptool -p && break + info "Waiting for lldpad to be ready" + sleep 1 + i=$((i + 1)) + done + + while [ $i -lt 60 ]; do + dcbtool sc "$netif" dcb on && break + info "Retrying to turn dcb on" + sleep 1 + i=$((i + 1)) + done + + while [ $i -lt 60 ]; do + dcbtool sc "$netif" pfc e:1 a:1 w:1 && break + info "Retrying to turn dcb on" + sleep 1 + i=$((i + 1)) + done + + while [ $i -lt 60 ]; do + dcbtool sc "$netif" app:fcoe e:1 a:1 w:1 && break + info "Retrying to turn fcoe on" + sleep 1 + i=$((i + 1)) + done + + sleep 1 +fi +write_fcoemon_cfg +fcoemon $syslogopt + +need_shutdown diff --git a/modules.d/95fcoe/lldpad.sh b/modules.d/95fcoe/lldpad.sh new file mode 100755 index 0000000..f992ae2 --- /dev/null +++ b/modules.d/95fcoe/lldpad.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if ! getargbool 1 rd.fcoe -d -n rd.nofcoe; then + info "rd.fcoe=0: skipping lldpad activation" + return 0 +fi + +# Note lldpad will stay running after switchroot, the system initscripts +# are to kill it and start a new lldpad to take over. Data is transferred +# between the 2 using a shm segment +lldpad -d +# wait for lldpad to be ready +i=0 +while [ $i -lt 60 ]; do + lldptool -p && break + info "Waiting for lldpad to be ready" + sleep 1 + i=$((i + 1)) +done diff --git a/modules.d/95fcoe/module-setup.sh b/modules.d/95fcoe/module-setup.sh new file mode 100755 index 0000000..3de85c2 --- /dev/null +++ b/modules.d/95fcoe/module-setup.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +# called by dracut +check() { + is_fcoe() { + block_is_fcoe "$1" || return 1 + } + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for_each_host_dev_and_slaves is_fcoe || return 255 + } + + require_binaries dcbtool fipvlan lldpad ip readlink fcoemon fcoeadm tr || return 1 + return 0 +} + +# called by dracut +depends() { + echo network rootfs-block + return 0 +} + +# called by dracut +installkernel() { + instmods fcoe libfcoe 8021q edd bnx2fc +} + +get_vlan_parent() { + local link=$1 + + [ -d "$link" ] || return + read -r iflink < "$link"/iflink + for if in /sys/class/net/*; do + read -r idx < "$if"/ifindex + if [ "$idx" -eq "$iflink" ]; then + echo "${if##*/}" + fi + done +} + +# called by dracut +cmdline() { + { + for c in /sys/bus/fcoe/devices/ctlr_*; do + [ -L "$c" ] || continue + read -r enabled < "$c"/enabled + read -r mode < "$c"/mode + [ "$enabled" -eq 0 ] && continue + if [ "$mode" = "VN2VN" ]; then + mode="vn2vn" + else + mode="fabric" + fi + d=$( + cd -P "$c" || exit + echo "$PWD" + ) + i=${d%/*} + ifname=${i##*/} + read -r mac < "${i}"/address + s=$(dcbtool gc "${i##*/}" dcb 2> /dev/null | sed -n 's/^DCB State:\t*\(.*\)/\1/p') + if [ -z "$s" ]; then + p=$(get_vlan_parent "${i}") + if [ "$p" ]; then + s=$(dcbtool gc "${p}" dcb 2> /dev/null | sed -n 's/^DCB State:\t*\(.*\)/\1/p') + ifname=${p##*/} + fi + fi + if [ "$s" = "on" ]; then + dcb="dcb" + else + dcb="nodcb" + fi + + # Some Combined Network Adapters(CNAs) implement DCB in firmware. + # Do not run software-based DCB or LLDP on CNAs that implement DCB. + # If the network interface provides hardware DCB/DCBX capabilities, + # DCB_REQUIRED in "/etc/fcoe/cfg-xxx" is expected to set to "no". + # + # Force "nodcb" if there's any DCB_REQUIRED="no"(child or vlan parent). + if grep -q '^[[:blank:]]*DCB_REQUIRED="no"' /etc/fcoe/cfg-"${i##*/}" &> /dev/null; then + dcb="nodcb" + fi + + if [ "$p" ]; then + if grep -q '^[[:blank:]]*DCB_REQUIRED="no"' /etc/fcoe/cfg-"${p}" &> /dev/null; then + dcb="nodcb" + fi + fi + + echo "ifname=${ifname}:${mac}" + echo "fcoe=${ifname}:${dcb}:${mode}" + done + } | sort | uniq +} + +# called by dracut +install() { + inst_multiple ip dcbtool fipvlan lldpad readlink lldptool fcoemon fcoeadm tr + if [[ -e $dracutsysrootdir/etc/hba.conf ]]; then + inst_libdir_file 'libhbalinux.so*' + inst_simple "/etc/hba.conf" + fi + + mkdir -m 0755 -p "$initdir/var/lib/lldpad" + mkdir -m 0755 -p "$initdir/etc/fcoe" + + if [[ $hostonly_cmdline == "yes" ]]; then + local _fcoeconf + _fcoeconf=$(cmdline) + [[ $_fcoeconf ]] && printf "%s\n" "$_fcoeconf" >> "${initdir}/etc/cmdline.d/95fcoe.conf" + fi + inst_multiple "/etc/fcoe/cfg-*" + + inst "$moddir/fcoe-up.sh" "/sbin/fcoe-up" + inst "$moddir/fcoe-edd.sh" "/sbin/fcoe-edd" + inst_hook pre-trigger 03 "$moddir/lldpad.sh" + inst_hook cmdline 99 "$moddir/parse-fcoe.sh" + inst_hook cleanup 90 "$moddir/cleanup-fcoe.sh" + inst_hook shutdown 40 "$moddir/stop-fcoe.sh" + dracut_need_initqueue +} diff --git a/modules.d/95fcoe/parse-fcoe.sh b/modules.d/95fcoe/parse-fcoe.sh new file mode 100755 index 0000000..bde6b62 --- /dev/null +++ b/modules.d/95fcoe/parse-fcoe.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Supported formats: +# fcoe=<networkdevice>:<dcb|nodcb>:<fabric|vn2vn> +# fcoe=<macaddress>:<dcb|nodcb>:<fabric|vn2vn> +# +# Note currently only nodcb is supported, the dcb option is reserved for +# future use. +# +# Note letters in the macaddress must be lowercase! +# +# Examples: +# fcoe=eth0:nodcb:vn2vn +# fcoe=4a:3f:4c:04:f8:d7:nodcb:fabric + +if [ -z "$fcoe" ] && ! getarg fcoe=; then + # If it's not set we don't continue + return 0 +fi + +if ! getargbool 1 rd.fcoe -d -n rd.nofcoe; then + info "rd.fcoe=0: skipping fcoe" + return 0 +fi + +if ! [ -e /sys/bus/fcoe/ctlr_create ] && ! modprobe -b -a fcoe && ! modprobe -b -a libfcoe; then + die "FCoE requested but kernel/initrd does not support FCoE" +fi + +initqueue --onetime modprobe -b -q bnx2fc + +parse_fcoe_opts() { + local fcoe_interface + local fcoe_dcb + local fcoe_mode + local fcoe_mac + local OLDIFS="$IFS" + local IFS=: + # shellcheck disable=SC2086 + # shellcheck disable=SC2048 + set -- $* + IFS="$OLDIFS" + + case $# in + 2) + fcoe_interface=$1 + fcoe_dcb=$2 + fcoe_mode="fabric" + unset fcoe_mac + ;; + 3) + fcoe_interface=$1 + fcoe_dcb=$2 + fcoe_mode=$3 + unset fcoe_mac + ;; + 7) + fcoe_mac=$(echo "$1:$2:$3:$4:$5:$6" | tr "[:upper:]" "[:lower:]") + fcoe_dcb=$7 + fcoe_mode="fabric" + unset fcoe_interface + ;; + 8) + fcoe_mac=$(echo "$1:$2:$3:$4:$5:$6" | tr "[:upper:]" "[:lower:]") + fcoe_dcb=$7 + fcoe_mode=$8 + unset fcoe_interface + ;; + *) + warn "Invalid arguments for fcoe=$fcoe" + return 1 + ;; + esac + + if [ "$fcoe_dcb" != "nodcb" -a "$fcoe_dcb" != "dcb" ]; then + warn "Invalid FCoE DCB option: $fcoe_dcb" + fi + + if [ "$fcoe_interface" = "edd" ]; then + /sbin/initqueue --settled --unique /sbin/fcoe-edd "$fcoe_dcb" + return 0 + fi + + if [ -z "$fcoe_interface" -a -z "$fcoe_mac" ]; then + warn "fcoe: Neither interface nor MAC specified for fcoe=$fcoe" + return 1 + fi + + { + if [ -n "$fcoe_mac" ]; then + # shellcheck disable=SC2016 + printf 'ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="%s", RUN+="/sbin/initqueue --onetime --unique --name fcoe-up-$name /sbin/fcoe-up $name %s %s"\n' "$fcoe_mac" "$fcoe_dcb" "$fcoe_mode" + # shellcheck disable=SC2016 + printf 'ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="%s", RUN+="/sbin/initqueue --onetime --timeout --unique --name fcoe-timeout-$name /sbin/fcoe-up $name %s %s"\n' "$fcoe_mac" "$fcoe_dcb" "$fcoe_mode" + else + # shellcheck disable=SC2016 + printf 'ACTION=="add", SUBSYSTEM=="net", NAME=="%s", RUN+="/sbin/initqueue --onetime --unique --name fcoe-up-$name /sbin/fcoe-up $name %s %s"\n' "$fcoe_interface" "$fcoe_dcb" "$fcoe_mode" + # shellcheck disable=SC2016 + printf 'ACTION=="add", SUBSYSTEM=="net", NAME=="%s", RUN+="/sbin/initqueue --onetime --timeout --unique --name fcoe-timeout-$name /sbin/fcoe-up $name %s %s"\n' "$fcoe_interface" "$fcoe_dcb" "$fcoe_mode" + fi + } >> /etc/udev/rules.d/92-fcoe.rules +} + +for fcoe in $fcoe $(getargs fcoe=); do + parse_fcoe_opts "$fcoe" +done diff --git a/modules.d/95fcoe/stop-fcoe.sh b/modules.d/95fcoe/stop-fcoe.sh new file mode 100755 index 0000000..a89cbbc --- /dev/null +++ b/modules.d/95fcoe/stop-fcoe.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +for f in /sys/bus/fcoe/devices/ctlr_*; do + [ -e "$f" ] || continue + echo 0 > "$f"/enabled +done diff --git a/modules.d/95fstab-sys/module-setup.sh b/modules.d/95fstab-sys/module-setup.sh new file mode 100755 index 0000000..e58f0d8 --- /dev/null +++ b/modules.d/95fstab-sys/module-setup.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# called by dracut +check() { + [[ -f $dracutsysrootdir/etc/fstab.sys ]] || [[ -n $add_fstab || -n $fstab_lines ]] +} + +# called by dracut +depends() { + echo fs-lib +} + +# called by dracut +install() { + [[ -f $dracutsysrootdir/etc/fstab.sys ]] && inst_simple /etc/fstab.sys + inst_hook pre-pivot 00 "$moddir/mount-sys.sh" +} diff --git a/modules.d/95fstab-sys/mount-sys.sh b/modules.d/95fstab-sys/mount-sys.sh new file mode 100755 index 0000000..bb4bcdb --- /dev/null +++ b/modules.d/95fstab-sys/mount-sys.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +type det_fs > /dev/null 2>&1 || . /lib/fs-lib.sh + +fstab_mount() { + local _dev _mp _fs _opts _pass + test -e "$1" || return 1 + info "Mounting from $1" + while read -r _dev _mp _fs _opts _ _pass _ || [ -n "$_dev" ]; do + [ -z "${_dev%%#*}" ] && continue # Skip comment lines + ismounted "$_mp" && continue # Skip mounted filesystem + if [ "$_pass" -gt 0 ] && ! strstr "$_opts" _netdev; then + fsck_single "$_dev" "$_fs" "$_opts" + fi + _fs=$(det_fs "$_dev" "$_fs") + info "Mounting $_dev" + if [ -d "$NEWROOT/$_mp" ]; then + mount -v -t "$_fs" -o "$_opts" "$_dev" "$NEWROOT/$_mp" 2>&1 | vinfo + else + [ -d "$_mp" ] || mkdir -p "$_mp" + mount -v -t "$_fs" -o "$_opts" "$_dev" "$_mp" 2>&1 | vinfo + fi + done < "$1" + return 0 +} + +# systemd will mount and run fsck from /etc/fstab and we don't want to +# run into a race condition. +if [ -z "$DRACUT_SYSTEMD" ]; then + [ -f /etc/fstab ] && fstab_mount /etc/fstab +fi + +# prefer $NEWROOT/etc/fstab.sys over local /etc/fstab.sys +if [ -f "$NEWROOT"/etc/fstab.sys ]; then + fstab_mount "$NEWROOT"/etc/fstab.sys +elif [ -f /etc/fstab.sys ]; then + fstab_mount /etc/fstab.sys +fi diff --git a/modules.d/95iscsi/cleanup-iscsi.sh b/modules.d/95iscsi/cleanup-iscsi.sh new file mode 100755 index 0000000..8338503 --- /dev/null +++ b/modules.d/95iscsi/cleanup-iscsi.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ -z "${DRACUT_SYSTEMD}" ] && { [ -e /sys/module/bnx2i ] || [ -e /sys/module/qedi ]; }; then + killproc iscsiuio +fi diff --git a/modules.d/95iscsi/iscsiroot.sh b/modules.d/95iscsi/iscsiroot.sh new file mode 100755 index 0000000..b6af7f4 --- /dev/null +++ b/modules.d/95iscsi/iscsiroot.sh @@ -0,0 +1,322 @@ +#!/bin/sh +# +# This implementation is incomplete: Discovery mode is not implemented and +# the argument handling doesn't follow currently agreed formats. This is mainly +# because rfc4173 does not say anything about iscsi_initiator but open-iscsi's +# iscsistart needs this. +# + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +type parse_iscsi_root > /dev/null 2>&1 || . /lib/net-lib.sh +type write_fs_tab > /dev/null 2>&1 || . /lib/fs-lib.sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +# Huh? Empty $1? +[ -z "$1" ] && exit 1 + +# Huh? Empty $2? +[ -z "$2" ] && exit 1 + +# Huh? Empty $3? This isn't really necessary, since NEWROOT isn't +# used here. But let's be consistent +[ -z "$3" ] && exit 1 + +# root is in the form root=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname> +netif="$1" +iroot="$2" + +# If it's not iscsi we don't continue +[ "${iroot%%:*}" = "iscsi" ] || exit 1 + +iroot=${iroot#iscsi} +iroot=${iroot#:} + +# XXX modprobe crc32c should go in the cmdline parser, but I haven't yet +# figured out a way how to check whether this is built-in or not +modprobe crc32c 2> /dev/null + +# start iscsiuio if needed +if [ -z "${DRACUT_SYSTEMD}" ] \ + && { [ -e /sys/module/bnx2i ] || [ -e /sys/module/qedi ]; } \ + && ! [ -e /tmp/iscsiuio-started ]; then + iscsiuio + : > /tmp/iscsiuio-started +fi + +handle_firmware() { + local ifaces retry _res + + # Depending on the 'ql4xdisablesysfsboot' qla4xxx + # will be autostarting sessions without presenting + # them via the firmware interface. + # In these cases 'iscsiadm -m fw' will fail, but + # the iSCSI sessions will still be present. + if ! iscsiadm -m fw; then + warn "iscsiadm: Could not get list of targets from firmware." + else + ifaces=$( + set -- /sys/firmware/ibft/ethernet* + echo $# + ) + read -r retry < /tmp/session-retry + + if [ "$retry" -lt "$ifaces" ]; then + retry=$((retry + 1)) + echo $retry > /tmp/session-retry + return 1 + else + rm /tmp/session-retry + fi + + # check to see if we have the new iscsiadm command, + # that supports the "no-wait" (-W) flag. If so, use it. + iscsiadm -m fw -l -W 2> /dev/null + _res=$? + if [ $_res -eq 7 ]; then + # ISCSI_ERR_INVALID (7) => "-W" not supported + info "iscsiadm does not support no-wait firmware logins" + iscsiadm -m fw -l + _res=$? + fi + if [ $_res -ne 0 ]; then + warn "iscsiadm: Log-in to iscsi target failed" + else + need_shutdown + fi + fi + [ -d /sys/class/iscsi_session ] || return 1 + echo 'started' > "/tmp/iscsistarted-iscsi:" + echo 'started' > "/tmp/iscsistarted-firmware" + + return 0 +} + +handle_netroot() { + local iscsi_initiator iscsi_target_name iscsi_target_ip iscsi_target_port + local iscsi_target_group iscsirw iscsi_lun + local iscsi_username iscsi_password + local iscsi_in_username iscsi_in_password + local iscsi_iface_name iscsi_netdev_name + local iscsi_param param + local p found + local login_retry_max_seen= + + # override conf settings by command line options + arg=$(getarg rd.iscsi.initiator -d iscsi_initiator=) + [ -n "$arg" ] && iscsi_initiator=$arg + arg=$(getarg rd.iscsi.target.group -d iscsi_target_group=) + [ -n "$arg" ] && iscsi_target_group=$arg + arg=$(getarg rd.iscsi.username -d iscsi_username=) + [ -n "$arg" ] && iscsi_username=$arg + arg=$(getarg rd.iscsi.password -d iscsi_password) + [ -n "$arg" ] && iscsi_password=$arg + arg=$(getarg rd.iscsi.in.username -d iscsi_in_username=) + [ -n "$arg" ] && iscsi_in_username=$arg + arg=$(getarg rd.iscsi.in.password -d iscsi_in_password=) + [ -n "$arg" ] && iscsi_in_password=$arg + for p in $(getargs rd.iscsi.param -d iscsi_param); do + [ "${p%=*}" = node.session.initial_login_retry_max ] \ + && login_retry_max_seen=yes + iscsi_param="$iscsi_param $p" + done + + # this sets iscsi_target_name and possibly overwrites most + # parameters read from the command line above + parse_iscsi_root "$1" || return 1 + + # Bail out early, if there is no route to the destination + if is_ip "$iscsi_target_ip" && [ "$netif" != "timeout" ] && ! all_ifaces_setup && getargbool 1 rd.iscsi.testroute; then + ip route get "$iscsi_target_ip" > /dev/null 2>&1 || return 0 + fi + + #limit iscsistart login retries + if [ "$login_retry_max_seen" != yes ]; then + retries=$(getargnum 3 0 10000 rd.iscsi.login_retry_max) + if [ "$retries" -gt 0 ]; then + iscsi_param="${iscsi_param% } node.session.initial_login_retry_max=$retries" + fi + fi + + # XXX is this needed? + getarg ro && iscsirw=ro + getarg rw && iscsirw=rw + fsopts=${fsopts:+$fsopts,}${iscsirw} + + if [ -z "$iscsi_initiator" ] && [ -f /sys/firmware/ibft/initiator/initiator-name ] && ! [ -f /tmp/iscsi_set_initiator ]; then + iscsi_initiator=$(while read -r line || [ -n "$line" ]; do echo "$line"; done < /sys/firmware/ibft/initiator/initiator-name) + echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi + rm -f /etc/iscsi/initiatorname.iscsi + mkdir -p /etc/iscsi + ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi + : > /tmp/iscsi_set_initiator + if [ -n "$DRACUT_SYSTEMD" ]; then + systemctl try-restart iscsid + # FIXME: iscsid is not yet ready, when the service is :-/ + sleep 1 + fi + fi + + if [ -z "$iscsi_initiator" ]; then + [ -f /run/initiatorname.iscsi ] && . /run/initiatorname.iscsi + [ -f /etc/initiatorname.iscsi ] && . /etc/initiatorname.iscsi + [ -f /etc/iscsi/initiatorname.iscsi ] && . /etc/iscsi/initiatorname.iscsi + iscsi_initiator=$InitiatorName + fi + + if [ -z "$iscsi_initiator" ]; then + iscsi_initiator=$(iscsi-iname) + echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi + rm -f /etc/iscsi/initiatorname.iscsi + mkdir -p /etc/iscsi + ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi + : > /tmp/iscsi_set_initiator + if [ -n "$DRACUT_SYSTEMD" ]; then + systemctl try-restart iscsid + # FIXME: iscsid is not yet ready, when the service is :-/ + sleep 1 + fi + fi + + if [ -z "$iscsi_target_port" ]; then + iscsi_target_port=3260 + fi + + if [ -z "$iscsi_target_group" ]; then + iscsi_target_group=1 + fi + + if [ -z "$iscsi_lun" ]; then + iscsi_lun=0 + fi + + echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi + ln -fs /run/initiatorname.iscsi /dev/.initiatorname.iscsi + if ! [ -e /etc/iscsi/initiatorname.iscsi ]; then + mkdir -p /etc/iscsi + ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi + if [ -n "$DRACUT_SYSTEMD" ]; then + systemctl try-restart iscsid + # FIXME: iscsid is not yet ready, when the service is :-/ + sleep 1 + fi + fi + + if [ -z "$DRACUT_SYSTEMD" ]; then + iscsid + sleep 2 + fi + + # FIXME $iscsi_protocol?? + + if [ "$root" = "dhcp" ] || [ "$netroot" = "dhcp" ]; then + # if root is not specified try to mount the whole iSCSI LUN + printf 'SYMLINK=="disk/by-path/*-iscsi-*-%s", SYMLINK+="root"\n' "$iscsi_lun" >> /etc/udev/rules.d/99-iscsi-root.rules + udevadm control --reload + write_fs_tab /dev/root + wait_for_dev -n /dev/root + + # install mount script + [ -z "$DRACUT_SYSTEMD" ] \ + && echo "iscsi_lun=$iscsi_lun . /bin/mount-lun.sh " > "$hookdir"/mount/01-$$-iscsi.sh + fi + + if strglobin "$iscsi_target_ip" '*:*:*' && ! strglobin "$iscsi_target_ip" '['; then + iscsi_target_ip="[$iscsi_target_ip]" + fi + targets=$(iscsiadm -m discovery -t st -p "$iscsi_target_ip":${iscsi_target_port:+$iscsi_target_port} | { + while read -r _ target _ || [ -n "$target" ]; do + echo "$target" + done + }) + [ -z "$targets" ] && warn "Target discovery to $iscsi_target_ip:${iscsi_target_port:+$iscsi_target_port} failed with status $?" && return 1 + + found= + for target in $targets; do + if [ "$target" = "$iscsi_target_name" ]; then + if [ -n "$iscsi_iface_name" ]; then + iscsiadm -m iface -I "$iscsi_iface_name" --op=new + EXTRA=" ${iscsi_netdev_name:+--name=iface.net_ifacename --value=$iscsi_netdev_name} " + EXTRA="$EXTRA ${iscsi_initiator:+--name=iface.initiatorname --value=$iscsi_initiator} " + fi + [ -n "$iscsi_param" ] && for param in $iscsi_param; do EXTRA="$EXTRA --name=${param%=*} --value=${param#*=}"; done + + CMD="iscsiadm -m node -T $target \ + ${iscsi_iface_name:+-I $iscsi_iface_name} \ + -p $iscsi_target_ip${iscsi_target_port:+:$iscsi_target_port}" + __op="--op=update \ + --name=node.startup --value=onboot \ + ${iscsi_username:+ --name=node.session.auth.username --value=$iscsi_username} \ + ${iscsi_password:+ --name=node.session.auth.password --value=$iscsi_password} \ + ${iscsi_in_username:+--name=node.session.auth.username_in --value=$iscsi_in_username} \ + ${iscsi_in_password:+--name=node.session.auth.password_in --value=$iscsi_in_password} \ + $EXTRA" + # shellcheck disable=SC2086 + $CMD $__op + if [ "$netif" != "timeout" ]; then + $CMD --login + fi + found=yes + break + fi + done + + if [ "$netif" = "timeout" ]; then + iscsiadm -m node -L onboot || : + elif [ "$found" != yes ]; then + warn "iSCSI target \"$iscsi_target_name\" not found on portal $iscsi_target_ip:$iscsi_target_port" + return 1 + fi + : > "$hookdir"/initqueue/work + + netroot_enc=$(str_replace "$1" '/' '\2f') + echo 'started' > "/tmp/iscsistarted-iscsi:${netroot_enc}" + return 0 +} + +ret=0 + +if [ "$netif" != "timeout" ] && getargbool 0 rd.iscsi.waitnet; then + all_ifaces_setup || exit 0 +fi + +if [ "$netif" = "timeout" ] && all_ifaces_setup; then + # s.th. went wrong and the timeout script hits + # restart + systemctl restart iscsid + # damn iscsid is not ready after unit says it's ready + sleep 2 +fi + +if getargbool 0 rd.iscsi.firmware -d -y iscsi_firmware; then + if [ "$netif" = "timeout" ] || [ "$netif" = "online" ] || [ "$netif" = "dummy" ]; then + [ -f /tmp/session-retry ] || echo 1 > /tmp/session-retry + handle_firmware + ret=$? + fi +fi + +if ! [ "$netif" = "online" ]; then + # loop over all netroot parameter + if nroot=$(getarg netroot) && [ "$nroot" != "dhcp" ]; then + for nroot in $(getargs netroot); do + [ "${nroot%%:*}" = "iscsi" ] || continue + nroot="${nroot##iscsi:}" + if [ -n "$nroot" ]; then + handle_netroot "$nroot" + ret=$((ret + $?)) + fi + done + else + if [ -n "$iroot" ]; then + handle_netroot "$iroot" + ret=$? + fi + fi +fi + +need_shutdown + +# now we have a root filesystem somewhere in /dev/sd* +# let the normal block handler handle root= +exit $ret diff --git a/modules.d/95iscsi/module-setup.sh b/modules.d/95iscsi/module-setup.sh new file mode 100755 index 0000000..2bea2fc --- /dev/null +++ b/modules.d/95iscsi/module-setup.sh @@ -0,0 +1,294 @@ +#!/bin/bash + +# called by dracut +check() { + # If our prerequisites are not met, fail anyways. + require_binaries iscsi-iname iscsiadm iscsid || return 1 + require_kernel_modules iscsi_tcp || return 1 + + # If hostonly was requested, fail the check if we are not actually + # booting from root. + [[ $hostonly ]] || [[ $mount_needs ]] && { + pushd . > /dev/null + for_each_host_dev_and_slaves block_is_iscsi + local _is_iscsi=$? + popd > /dev/null || exit + [[ $_is_iscsi == 0 ]] || return 255 + } + return 0 +} + +get_ibft_mod() { + local ibft_mac=$1 + local iface_mac iface_mod + # Return the iSCSI offload module for a given MAC address + for iface_desc in $(iscsiadm -m iface | cut -f 2 -d ' '); do + iface_mod=${iface_desc%%,*} + iface_mac=${iface_desc#*,} + iface_mac=${iface_mac%%,*} + if [ "$ibft_mac" = "$iface_mac" ]; then + echo "$iface_mod" + return 0 + fi + done +} + +install_ibft() { + # When iBFT / iscsi_boot is detected: + # - Use 'ip=ibft' to set up iBFT network interface + # Note: bnx2i is using a different MAC address of iSCSI offloading + # so the 'ip=ibft' parameter must not be set + # - specify firmware booting cmdline parameter + + for d in /sys/firmware/*; do + if [ -d "${d}"/ethernet0 ]; then + read -r ibft_mac < "${d}"/ethernet0/mac + ibft_mod=$(get_ibft_mod "$ibft_mac") + fi + if [ -z "$ibft_mod" ] && [ -d "${d}"/ethernet1 ]; then + read -r ibft_mac < "${d}"/ethernet1/mac + ibft_mod=$(get_ibft_mod "$ibft_mac") + fi + if [ -d "${d}"/initiator ]; then + if [ "${d##*/}" = "ibft" ] && [ "$ibft_mod" != "bnx2i" ]; then + echo -n "rd.iscsi.ibft=1 " + fi + echo -n "rd.iscsi.firmware=1 " + fi + done +} + +install_iscsiroot() { + local devpath=$1 + local scsi_path iscsi_lun session c d conn host flash + local iscsi_session iscsi_address iscsi_port iscsi_targetname + + scsi_path=${devpath%%/block*} + [ "$scsi_path" = "$devpath" ] && return 1 + iscsi_lun=${scsi_path##*:} + [ "$iscsi_lun" = "$scsi_path" ] && return 1 + session=${devpath%%/target*} + [ "$session" = "$devpath" ] && return 1 + iscsi_session=${session##*/} + [ "$iscsi_session" = "$session" ] && return 1 + host=${session%%/session*} + [ "$host" = "$session" ] && return 1 + iscsi_host=${host##*/} + + for flash in "${host}"/flashnode_sess-*; do + [ -f "$flash" ] || continue + [ ! -e "$flash/is_boot_target" ] && continue + read -r is_boot < "$flash"/is_boot_target + if [ "$is_boot" -eq 1 ]; then + # qla4xxx flashnode session; skip iBFT discovery + read -r iscsi_initiator < /sys/class/iscsi_host/"${iscsi_host}"/initiatorname + echo "rd.iscsi.initiator=${iscsi_initiator}" + return + fi + done + + for d in "${session}"/*; do + case $d in + *connection*) + c=${d##*/} + conn=${d}/iscsi_connection/${c} + if [ -d "${conn}" ]; then + read -r iscsi_address < "${conn}"/persistent_address + read -r iscsi_port < "${conn}"/persistent_port + fi + ;; + *session) + if [ -d "${d}"/"${iscsi_session}" ]; then + read -r iscsi_initiator < "${d}"/"${iscsi_session}"/initiatorname + read -r iscsi_targetname < "${d}"/"${iscsi_session}"/targetname + fi + ;; + esac + done + + [ -z "$iscsi_address" ] && return + ip_params_for_remote_addr "$iscsi_address" + + if [ -n "$iscsi_address" -a -n "$iscsi_targetname" ]; then + if [ -n "$iscsi_port" -a "$iscsi_port" -eq 3260 ]; then + iscsi_port= + fi + if [ -n "$iscsi_lun" -a "$iscsi_lun" -eq 0 ]; then + iscsi_lun= + fi + # In IPv6 case rd.iscsi.initatior= must pass address in [] brackets + case "$iscsi_address" in + *:*) + iscsi_address="[$iscsi_address]" + ;; + esac + # Must be two separate lines, so that "sort | uniq" commands later + # can sort out rd.iscsi.initiator= duplicates + echo "rd.iscsi.initiator=${iscsi_initiator}" + echo "netroot=iscsi:${iscsi_address}::${iscsi_port}:${iscsi_lun}:${iscsi_targetname}" + echo "rd.neednet=1" + fi + return 0 +} + +install_softiscsi() { + [ -d /sys/firmware/ibft ] && return 0 + + is_softiscsi() { + local _dev=$1 + local iscsi_dev + + [[ -L "/sys/dev/block/$_dev" ]] || return + iscsi_dev=$( + cd -P /sys/dev/block/"$_dev" || exit + echo "$PWD" + ) + install_iscsiroot "$iscsi_dev" + } + + for_each_host_dev_and_slaves_all is_softiscsi || return 255 + return 0 +} + +# called by dracut +depends() { + echo network rootfs-block +} + +# called by dracut +installkernel() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + local _funcs='iscsi_register_transport' + + instmods bnx2i qla4xxx cxgb3i cxgb4i be2iscsi qedi + hostonly="" instmods iscsi_tcp iscsi_ibft crc32c iscsi_boot_sysfs 8021q + + if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then + _s390drivers="=drivers/s390/scsi" + fi + + dracut_instmods -o -s ${_funcs} =drivers/scsi ${_s390drivers:+"$_s390drivers"} +} + +# called by dracut +cmdline() { + local _iscsiconf + _iscsiconf=$(install_ibft) + { + if [ "$_iscsiconf" ]; then + echo "${_iscsiconf}" + else + install_softiscsi + fi + } | sort | uniq +} + +# called by dracut +install() { + inst_multiple -o iscsiuio + inst_libdir_file 'libgcc_s.so*' + inst_multiple umount iscsi-iname iscsiadm iscsid + inst_binary sort + + inst_multiple -o \ + "$systemdsystemunitdir"/iscsid.socket \ + "$systemdsystemunitdir"/iscsid.service \ + "$systemdsystemunitdir"/iscsiuio.service \ + "$systemdsystemunitdir"/iscsiuio.socket \ + "$systemdsystemunitdir"/sockets.target.wants/iscsid.socket \ + "$systemdsystemunitdir"/sockets.target.wants/iscsiuio.socket + + if [[ $hostonly ]]; then + local -a _filenames + + inst_dir /etc/iscsi + mapfile -t -d '' _filenames < <(find /etc/iscsi -type f -print0) + inst_multiple "${_filenames[@]}" + else + inst_simple /etc/iscsi/iscsid.conf + fi + + # Detect iBFT and perform mandatory steps + if [[ $hostonly_cmdline == "yes" ]]; then + local _iscsiconf + _iscsiconf=$(cmdline) + [[ $_iscsiconf ]] && printf "%s\n" "$_iscsiconf" >> "${initdir}/etc/cmdline.d/95iscsi.conf" + fi + + inst_hook cmdline 90 "$moddir/parse-iscsiroot.sh" + inst_hook cleanup 90 "$moddir/cleanup-iscsi.sh" + inst "$moddir/iscsiroot.sh" "/sbin/iscsiroot" + + if ! dracut_module_included "systemd"; then + inst "$moddir/mount-lun.sh" "/bin/mount-lun.sh" + else + inst_multiple -o \ + "$systemdsystemunitdir"/iscsi.service \ + "$systemdsystemunitdir"/iscsi-init.service \ + "$systemdsystemunitdir"/iscsid.service \ + "$systemdsystemunitdir"/iscsid.socket \ + "$systemdsystemunitdir"/iscsiuio.service \ + "$systemdsystemunitdir"/iscsiuio.socket \ + iscsiadm iscsid + + for i in \ + iscsid.socket \ + iscsiuio.socket; do + $SYSTEMCTL -q --root "$initdir" enable "$i" + done + + mkdir -p "${initdir}/$systemdsystemunitdir/iscsid.service.d" + { + echo "[Unit]" + echo "DefaultDependencies=no" + echo "Conflicts=shutdown.target" + echo "Before=shutdown.target" + } > "${initdir}/$systemdsystemunitdir/iscsid.service.d/dracut.conf" + + mkdir -p "${initdir}/$systemdsystemunitdir/iscsid.socket.d" + { + echo "[Unit]" + echo "DefaultDependencies=no" + echo "Conflicts=shutdown.target" + echo "Before=shutdown.target sockets.target" + } > "${initdir}/$systemdsystemunitdir/iscsid.socket.d/dracut.conf" + + mkdir -p "${initdir}/$systemdsystemunitdir/iscsiuio.service.d" + { + echo "[Unit]" + echo "DefaultDependencies=no" + echo "Conflicts=shutdown.target" + echo "Before=shutdown.target" + } > "${initdir}/$systemdsystemunitdir/iscsiuio.service.d/dracut.conf" + + mkdir -p "${initdir}/$systemdsystemunitdir/iscsiuio.socket.d" + { + echo "[Unit]" + echo "DefaultDependencies=no" + echo "Conflicts=shutdown.target" + echo "Before=shutdown.target sockets.target" + } > "${initdir}/$systemdsystemunitdir/iscsiuio.socket.d/dracut.conf" + + # Fedora 34 iscsid requires iscsi-shutdown.service + # which would terminate all iSCSI connections on switch root + cat > "${initdir}/$systemdsystemunitdir/iscsi-shutdown.service" << EOF +[Unit] +Description=Dummy iscsi-shutdown.service for the initrd +Documentation=man:iscsid(8) man:iscsiadm(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service +Before=remote-fs-pre.target + +[Service] +Type=oneshot +RemainAfterExit=false +ExecStart=-/usr/bin/true +EOF + fi + inst_dir /var/lib/iscsi + mkdir -p "${initdir}/var/lib/iscsi/nodes" + # Fedora 34 iscsid wants a non-empty /var/lib/iscsi/nodes directory + : > "${initdir}/var/lib/iscsi/nodes/.dracut" + dracut_need_initqueue +} diff --git a/modules.d/95iscsi/mount-lun.sh b/modules.d/95iscsi/mount-lun.sh new file mode 100755 index 0000000..c186984 --- /dev/null +++ b/modules.d/95iscsi/mount-lun.sh @@ -0,0 +1,15 @@ +#!/bin/sh +if [ -z "$iscsi_lun" ]; then + iscsi_lun=0 +fi +NEWROOT=${NEWROOT:-/sysroot} + +for disk in /dev/disk/by-path/*-iscsi-*-"$iscsi_lun"; do + if mount -t "${fstype:-auto}" -o "$rflags" "$disk" "$NEWROOT"; then + if [ ! -d "$NEWROOT"/proc ]; then + umount "$disk" + continue + fi + break + fi +done diff --git a/modules.d/95iscsi/parse-iscsiroot.sh b/modules.d/95iscsi/parse-iscsiroot.sh new file mode 100755 index 0000000..7574711 --- /dev/null +++ b/modules.d/95iscsi/parse-iscsiroot.sh @@ -0,0 +1,156 @@ +#!/bin/sh +# +# Preferred format: +# root=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname> +# [root=*] netroot=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname> +# +# Legacy formats: +# [net]root=[iscsi] iscsiroot=[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname> +# [net]root=[iscsi] iscsi_firmware +# +# root= takes precedence over netroot= if root=iscsi[...] +# + +# This script is sourced, so root should be set. But let's be paranoid +[ -z "$root" ] && root=$(getarg root=) +if [ -z "$netroot" ]; then + for nroot in $(getargs netroot=); do + [ "${nroot%%:*}" = "iscsi" ] && break + done + if [ "${nroot%%:*}" = "iscsi" ]; then + netroot="$nroot" + else + for nroot in $(getargs netroot=); do + [ "${nroot%%:*}" = "dhcp" ] && break + done + netroot="$nroot" + fi +fi +[ -z "$iscsiroot" ] && iscsiroot=$(getarg iscsiroot=) +[ -z "$iscsi_firmware" ] && getargbool 0 rd.iscsi.firmware -y iscsi_firmware && iscsi_firmware="1" + +[ -n "$iscsiroot" ] && [ -n "$iscsi_firmware" ] && die "Mixing iscsiroot and iscsi_firmware is dangerous" + +type write_fs_tab > /dev/null 2>&1 || . /lib/fs-lib.sh + +# Root takes precedence over netroot +if [ "${root%%:*}" = "iscsi" ]; then + if [ -n "$netroot" ]; then + echo "Warning: root takes precedence over netroot. Ignoring netroot" + fi + netroot=$root + # if root is not specified try to mount the whole iSCSI LUN + printf 'ENV{DEVTYPE}!="partition", SYMLINK=="disk/by-path/*-iscsi-*-*", SYMLINK+="root"\n' >> /etc/udev/rules.d/99-iscsi-root.rules + [ -n "$DRACUT_SYSTEMD" ] && systemctl is-active systemd-udevd && udevadm control --reload-rules + root=/dev/root + + write_fs_tab /dev/root +fi + +# If it's not empty or iscsi we don't continue +for nroot in $(getargs netroot); do + [ "${nroot%%:*}" = "iscsi" ] || continue + netroot="$nroot" + break +done + +# Root takes precedence over netroot +if [ "${root}" = "/dev/root" ] && getarg "netroot=dhcp"; then + # if root is not specified try to mount the whole iSCSI LUN + printf 'ENV{DEVTYPE}!="partition", SYMLINK=="disk/by-path/*-iscsi-*-*", SYMLINK+="root"\n' >> /etc/udev/rules.d/99-iscsi-root.rules + [ -n "$DRACUT_SYSTEMD" ] && systemctl is-active systemd-udevd && udevadm control --reload-rules +fi + +if [ -n "$iscsiroot" ]; then + [ -z "$netroot" ] && netroot=$root + + # @deprecated + echo "Warning: Argument iscsiroot is deprecated and might be removed in a future" + echo "release. See 'man dracut.kernel' for more information." + + # Accept iscsiroot argument? + [ -z "$netroot" ] || [ "$netroot" = "iscsi" ] \ + || die "Argument iscsiroot only accepted for empty root= or [net]root=iscsi" + + # Override root with iscsiroot content? + [ -z "$netroot" ] || [ "$netroot" = "iscsi" ] && netroot=iscsi:$iscsiroot +fi + +# iscsi_firmware does not need argument checking +if [ -n "$iscsi_firmware" ]; then + if [ "$root" != "dhcp" ] && [ "$netroot" != "dhcp" ]; then + [ -z "$netroot" ] && netroot=iscsi: + fi + modprobe -b -q iscsi_boot_sysfs 2> /dev/null + modprobe -b -q iscsi_ibft + # if no ip= is given, but firmware + echo "${DRACUT_SYSTEMD+systemctl is-active initrd-root-device.target || }[ -f '/tmp/iscsistarted-firmware' ]" > "$hookdir"/initqueue/finished/iscsi_started.sh + initqueue --unique --online /sbin/iscsiroot online "iscsi:" "$NEWROOT" + initqueue --unique --onetime --timeout /sbin/iscsiroot timeout "iscsi:" "$NEWROOT" + initqueue --unique --onetime --settled /sbin/iscsiroot online "iscsi:" "'$NEWROOT'" +fi + +# ISCSI actually supported? +if ! [ -e /sys/module/iscsi_tcp ]; then + modprobe -b -q iscsi_tcp || die "iscsiroot requested but kernel/initrd does not support iscsi" +fi + +modprobe --all -b -q qla4xxx cxgb3i cxgb4i bnx2i be2iscsi + +if [ -n "$netroot" ] && [ "$root" != "/dev/root" ] && [ "$root" != "dhcp" ]; then + if ! getargbool 1 rd.neednet > /dev/null || ! getarg "ip="; then + initqueue --unique --onetime --settled /sbin/iscsiroot dummy "'$netroot'" "'$NEWROOT'" + fi +fi + +if arg=$(getarg rd.iscsi.initiator -d iscsi_initiator=) && [ -n "$arg" ] && ! [ -f /run/initiatorname.iscsi ]; then + iscsi_initiator=$arg + echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi + ln -fs /run/initiatorname.iscsi /dev/.initiatorname.iscsi + rm -f /etc/iscsi/initiatorname.iscsi + mkdir -p /etc/iscsi + ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi + if [ -n "$DRACUT_SYSTEMD" ]; then + systemctl try-restart iscsid + # FIXME: iscsid is not yet ready, when the service is :-/ + sleep 1 + fi +fi + +# If not given on the cmdline and initiator-name available via iBFT +if [ -z "$iscsi_initiator" ] && [ -f /sys/firmware/ibft/initiator/initiator-name ] && ! [ -f /tmp/iscsi_set_initiator ]; then + iscsi_initiator=$(while read -r line || [ -n "$line" ]; do echo "$line"; done < /sys/firmware/ibft/initiator/initiator-name) + if [ -n "$iscsi_initiator" ]; then + echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi + rm -f /etc/iscsi/initiatorname.iscsi + mkdir -p /etc/iscsi + ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi + : > /tmp/iscsi_set_initiator + if [ -n "$DRACUT_SYSTEMD" ]; then + systemctl try-restart iscsid + # FIXME: iscsid is not yet ready, when the service is :-/ + sleep 1 + fi + fi +fi + +if [ -z "$netroot" ] || ! [ "${netroot%%:*}" = "iscsi" ]; then + return 1 +fi + +initqueue --unique --onetime --timeout /sbin/iscsiroot timeout "$netroot" "$NEWROOT" + +for nroot in $(getargs netroot); do + [ "${nroot%%:*}" = "iscsi" ] || continue + type parse_iscsi_root > /dev/null 2>&1 || . /lib/net-lib.sh + parse_iscsi_root "$nroot" || return 1 + netroot_enc=$(str_replace "$nroot" '/' '\2f') + echo "${DRACUT_SYSTEMD+systemctl is-active initrd-root-device.target || }[ -f '/tmp/iscsistarted-$netroot_enc' ]" > "$hookdir"/initqueue/finished/iscsi_started.sh +done + +# Done, all good! +# shellcheck disable=SC2034 +rootok=1 + +# Shut up init error check +[ -z "$root" ] && root="iscsi" diff --git a/modules.d/95lunmask/fc_transport_scan_lun.sh b/modules.d/95lunmask/fc_transport_scan_lun.sh new file mode 100755 index 0000000..d14d2ca --- /dev/null +++ b/modules.d/95lunmask/fc_transport_scan_lun.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# +# fc_transport_lun_scan +# +# Selectively enable individual LUNs behind an FC remote port +# +# ACTION=="add", SUBSYSTEM=="fc_transport", ATTR{port_name}=="wwpn", \ +# PROGRAM="fc_transport_lun_scan lun" +# + +[ -z "$DEVPATH" ] && exit 1 + +if [ -n "$1" ]; then + LUN=$1 +else + LUN=- +fi +ID=${DEVPATH##*/rport-} +HOST=${ID%%:*} +CHANNEL=${ID%%-*} +CHANNEL=${CHANNEL#*:} +if [ -f /sys"$DEVPATH"/scsi_target_id ]; then + read -r TARGET < /sys"$DEVPATH"/scsi_target_id +fi +[ -z "$TARGET" ] && exit 1 +echo "$CHANNEL" "$TARGET" $LUN > /sys/class/scsi_host/host"$HOST"/scan diff --git a/modules.d/95lunmask/module-setup.sh b/modules.d/95lunmask/module-setup.sh new file mode 100755 index 0000000..cf6e3d7 --- /dev/null +++ b/modules.d/95lunmask/module-setup.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +# called by dracut +cmdline() { + get_lunmask() { + local _dev=$1 + local _devpath _sdev _lun _rport _end_device _classdev _wwpn _sas_address + _devpath=$( + cd -P /sys/dev/block/"$_dev" || exit + echo "$PWD" + ) + + [ "${_devpath#*/sd}" == "$_devpath" ] && return 1 + _sdev="${_devpath%%/block/*}" + _lun="${_sdev##*:}" + # Check for FibreChannel + _rport="${_devpath##*/rport-}" + if [ "$_rport" != "$_devpath" ]; then + _rport="${_rport%%/*}" + _classdev="/sys/class/fc_remote_ports/rport-${_rport}" + [ -d "$_classdev" ] || return 1 + read -r _wwpn < "${_classdev}"/port_name + echo "rd.lunmask=fc,${_wwpn},${_lun}" + return 0 + fi + # Check for SAS + _end_device="${_devpath##*/end_device-}" + if [ "$_end_device" != "$_devpath" ]; then + _end_device="${_end_device%%/*}" + _classdev="/sys/class/sas_device/end_device-${_end_device}" + [ -e "$_classdev" ] || return 1 + read -r _sas_address < "${_classdev}"/sas_address + echo "rd.lunmask=sas,${_sas_address},${_lun}" + return 0 + fi + return 1 + } + [[ $hostonly ]] || [[ $mount_needs ]] && { + for_each_host_dev_and_slaves_all get_lunmask + } | sort | uniq +} + +# called by dracut +check() { + [[ $hostonly ]] || [[ $mount_needs ]] && { + [ -w /sys/module/scsi_mod/parameters/scan ] || return 255 + read -r scan_type < /sys/module/scsi_mod/parameters/scan + [ "$scan_type" = "manual" ] && return 0 + return 255 + } + return 0 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_script "$moddir/fc_transport_scan_lun.sh" /usr/lib/udev/fc_transport_scan_lun.sh + inst_script "$moddir/sas_transport_scan_lun.sh" /usr/lib/udev/sas_transport_scan_lun.sh + inst_hook cmdline 30 "$moddir/parse-lunmask.sh" + if [[ $hostonly_cmdline == "yes" ]]; then + local _lunmask + + for _lunmask in $(cmdline); do + printf "%s\n" "$_lunmask" >> "${initdir}/etc/cmdline.d/95lunmask.conf" + done + fi +} diff --git a/modules.d/95lunmask/parse-lunmask.sh b/modules.d/95lunmask/parse-lunmask.sh new file mode 100755 index 0000000..5e05e5b --- /dev/null +++ b/modules.d/95lunmask/parse-lunmask.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +create_udev_rule() { + local transport="$1" + local tgtid="$2" + local lun="$3" + local _rule=/etc/udev/rules.d/51-"${transport}"-lunmask-"${tgtid}".rules + + [ -e "${_rule}" ] && return 0 + + if [ ! -f "$_rule" ]; then + if [ "$transport" = "fc" ]; then + cat > "$_rule" << EOF +ACTION=="add", SUBSYSTEM=="fc_remote_ports", ATTR{port_name}=="$tgtid", PROGRAM="fc_transport_scan_lun.sh $lun" +EOF + elif [ "$transport" = "sas" ]; then + cat > "$_rule" << EOF +ACTION=="add", SUBSYSTEM=="sas_device", ATTR{sas_address}=="$tgtid", PROGRAM="sas_transport_scan_lun.sh $lun" +EOF + fi + fi +} + +for lunmask_arg in $(getargs rd.lunmask); do + ( + local OLDIFS="$IFS" + local IFS="," + # shellcheck disable=SC2086 + set $lunmask_arg + IFS="$OLDIFS" + if [ -d /sys/module/scsi_mod ]; then + printf "manual" > /sys/module/scsi_mod/parameters/scan + elif [ ! -f /etc/modprobe.d/95lunmask.conf ]; then + echo "options scsi_mod scan=manual" > /etc/modprobe.d/95lunmask.conf + fi + create_udev_rule "$1" "$2" "$3" + ) +done diff --git a/modules.d/95lunmask/sas_transport_scan_lun.sh b/modules.d/95lunmask/sas_transport_scan_lun.sh new file mode 100755 index 0000000..1d1fc47 --- /dev/null +++ b/modules.d/95lunmask/sas_transport_scan_lun.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# +# sas_transport_lun_scan +# +# Selectively enable individual LUNs behind a SAS end device +# +# ACTION=="add", SUBSYSTEM=="sas_transport", ATTR{sas_address}=="sas_addr", \ +# PROGRAM="sas_transport_lun_scan lun" +# + +[ -z "$DEVPATH" ] && exit 1 + +if [ -n "$1" ]; then + LUN=$1 +else + LUN=- +fi +ID=${DEVPATH##*/end_device-} +HOST=${ID%%:*} +CHANNEL=${ID%%-*} +CHANNEL=${CHANNEL#*:} +if [ -f /sys"$DEVPATH"/scsi_target_id ]; then + read -r TARGET < /sys"$DEVPATH"/scsi_target_id +fi +[ -z "$TARGET" ] && exit 1 +echo 0 "$TARGET" $LUN > /sys/class/scsi_host/host"$HOST"/scan diff --git a/modules.d/95nbd/module-setup.sh b/modules.d/95nbd/module-setup.sh new file mode 100755 index 0000000..aa3570a --- /dev/null +++ b/modules.d/95nbd/module-setup.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# called by dracut +check() { + local _rootdev + + # if an nbd device is not somewhere in the chain of devices root is + # mounted on, fail the hostonly check. + [[ $hostonly ]] || [[ $mount_needs ]] && { + _rootdev=$(find_root_block_device) + [[ -b /dev/block/$_rootdev ]] || return 1 + check_block_and_slaves block_is_nbd "$_rootdev" || return 255 + } + require_binaries nbd-client || return 1 + + return 0 +} + +# called by dracut +depends() { + # We depend on network modules being loaded + echo network rootfs-block +} + +# called by dracut +installkernel() { + instmods nbd +} + +# called by dracut +install() { + inst nbd-client + inst_hook cmdline 90 "$moddir/parse-nbdroot.sh" + inst_script "$moddir/nbdroot.sh" "/sbin/nbdroot" + if dracut_module_included "systemd-initrd"; then + inst_script "$moddir/nbd-generator.sh" "$systemdutildir"/system-generators/dracut-nbd-generator + fi + dracut_need_initqueue +} diff --git a/modules.d/95nbd/nbd-generator.sh b/modules.d/95nbd/nbd-generator.sh new file mode 100755 index 0000000..38603bf --- /dev/null +++ b/modules.d/95nbd/nbd-generator.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +[ -z "$root" ] && root=$(getarg root=) + +[ "${root%%:*}" = "nbd" ] || exit 0 + +GENERATOR_DIR="$2" +[ -z "$GENERATOR_DIR" ] && exit 1 + +[ -d "$GENERATOR_DIR" ] || mkdir -p "$GENERATOR_DIR" + +ROOTFLAGS="$(getarg rootflags)" + +nroot=${root#nbd:} +nbdserver=${nroot%%:*} +if [ "${nbdserver%"${nbdserver#?}"}" = "[" ]; then + nbdserver=${nroot#[} + nbdserver=${nbdserver%%]:*}\] + nroot=${nroot#*]:} +else + nroot=${nroot#*:} +fi +nbdport=${nroot%%:*} +nroot=${nroot#*:} +nbdfstype=${nroot%%:*} +nroot=${nroot#*:} +nbdflags=${nroot%%:*} + +if [ "$nbdflags" = "$nbdfstype" ]; then + unset nbdflags +fi +if [ "$nbdfstype" = "$nbdport" ]; then + unset nbdfstype +fi + +[ -n "$nbdflags" ] && ROOTFLAGS="$nbdflags" + +if getarg "ro"; then + if [ -n "$ROOTFLAGS" ]; then + ROOTFLAGS="$ROOTFLAGS,ro" + else + ROOTFLAGS="ro" + fi +fi + +if [ -n "$nbdfstype" ]; then + ROOTFSTYPE="$nbdfstype" +else + ROOTFSTYPE=$(getarg rootfstype=) || unset ROOTFSTYPE +fi + +{ + echo "[Unit]" + echo "Before=initrd-root-fs.target" + echo "[Mount]" + echo "Where=/sysroot" + echo "What=/dev/root" + [ -n "$ROOTFSTYPE" ] && echo "Type=${ROOTFSTYPE}" + [ -n "$ROOTFLAGS" ] && echo "Options=${ROOTFLAGS}" +} > "$GENERATOR_DIR"/sysroot.mount + +exit 0 diff --git a/modules.d/95nbd/nbdroot.sh b/modules.d/95nbd/nbdroot.sh new file mode 100755 index 0000000..b1a8030 --- /dev/null +++ b/modules.d/95nbd/nbdroot.sh @@ -0,0 +1,137 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +# Huh? Empty $1? +[ -z "$1" ] && exit 1 + +# Huh? Empty $2? +[ -z "$2" ] && exit 1 + +# Huh? Empty $3? +[ -z "$3" ] && exit 1 + +# root is in the form root=nbd:srv:port[:fstype[:rootflags[:nbdopts]]] +# shellcheck disable=SC2034 +netif="$1" +nroot="$2" +NEWROOT="$3" + +# If it's not nbd we don't continue +[ "${nroot%%:*}" = "nbd" ] || return + +nroot=${nroot#nbd:} +nbdserver=${nroot%%:*} +if [ "${nbdserver%"${nbdserver#?}"}" = "[" ]; then + nbdserver=${nroot#[} + nbdserver=${nbdserver%%]:*} + nroot=${nroot#*]:} +else + nroot=${nroot#*:} +fi +nbdport=${nroot%%:*} +nroot=${nroot#*:} +nbdfstype=${nroot%%:*} +nroot=${nroot#*:} +nbdflags=${nroot%%:*} +nbdopts=${nroot#*:} + +if [ "$nbdopts" = "$nbdflags" ]; then + unset nbdopts +fi +if [ "$nbdflags" = "$nbdfstype" ]; then + unset nbdflags +fi +if [ "$nbdfstype" = "$nbdport" ]; then + unset nbdfstype +fi +if [ -z "$nbdfstype" ]; then + nbdfstype=auto +fi + +# look through the NBD options and pull out the ones that need to +# go before the host etc. Append a ',' so we know we terminate the loop +nbdopts=${nbdopts}, +while [ -n "$nbdopts" ]; do + f=${nbdopts%%,*} + nbdopts=${nbdopts#*,} + if [ -z "$f" ]; then + break + fi + if [ -z "${f%bs=*}" -o -z "${f%timeout=*}" ]; then + preopts="$preopts $f" + continue + fi + opts="$opts $f" +done + +# look through the flags and see if any are overridden by the command line +nbdflags=${nbdflags}, +while [ -n "$nbdflags" ]; do + f=${nbdflags%%,*} + nbdflags=${nbdflags#*,} + if [ -z "$f" ]; then + break + fi + if [ "$f" = "ro" -o "$f" = "rw" ]; then + nbdrw=$f + continue + fi + fsopts=${fsopts:+$fsopts,}$f +done + +getarg ro && nbdrw=ro +getarg rw && nbdrw=rw +fsopts=${fsopts:+$fsopts,}${nbdrw} + +# XXX better way to wait for the device to be made? +i=0 +while [ ! -b /dev/nbd0 ]; do + [ $i -ge 20 ] && exit 1 + udevadm settle --exit-if-exists=/dev/nbd0 + i=$((i + 1)) +done + +# If we didn't get a root= on the command line, then we need to +# add the udev rules for mounting the nbd0 device +if [ "$root" = "block:/dev/root" -o "$root" = "dhcp" ]; then + printf 'KERNEL=="nbd0", ENV{DEVTYPE}!="partition", ENV{ID_FS_TYPE}=="?*", SYMLINK+="root"\n' > /etc/udev/rules.d/99-nbd-root.rules + udevadm control --reload + wait_for_dev -n /dev/root + + if [ -z "$DRACUT_SYSTEMD" ]; then + type write_fs_tab > /dev/null 2>&1 || . /lib/fs-lib.sh + + write_fs_tab /dev/root "$nbdfstype" "$fsopts" + + printf '/bin/mount %s\n' \ + "$NEWROOT" \ + > "$hookdir"/mount/01-$$-nbd.sh + else + mkdir -p /run/systemd/system/sysroot.mount.d + cat << EOF > /run/systemd/system/sysroot.mount.d/dhcp.conf +[Mount] +Where=/sysroot +What=/dev/root +Type=$nbdfstype +Options=$fsopts +EOF + systemctl --no-block daemon-reload + fi + # if we're on systemd, use the nbd-generator script + # to create the /sysroot mount. +fi + +if ! [ "$nbdport" -gt 0 ] 2> /dev/null; then + nbdport="-name $nbdport" +fi + +if ! nbd-client -check /dev/nbd0 > /dev/null; then + # shellcheck disable=SC2086 + nbd-client -p -systemd-mark "$nbdserver" $nbdport /dev/nbd0 $opts || exit 1 +fi + +need_shutdown +exit 0 diff --git a/modules.d/95nbd/parse-nbdroot.sh b/modules.d/95nbd/parse-nbdroot.sh new file mode 100755 index 0000000..1c3f0a0 --- /dev/null +++ b/modules.d/95nbd/parse-nbdroot.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# +# Preferred format: +# root=nbd:srv:port/exportname[:fstype[:rootflags[:nbdopts]]] +# [root=*] netroot=nbd:srv:port/exportname[:fstype[:rootflags[:nbdopts]]] +# +# nbdopts is a comma separated list of options to give to nbd-client +# +# root= takes precedence over netroot= if root=nbd[...] +# + +# This script is sourced, so root should be set. But let's be paranoid +[ -z "$root" ] && root=$(getarg root=) + +if [ -z "$netroot" ]; then + for netroot in $(getargs netroot=); do + [ "${netroot%%:*}" = "nbd" ] && break + done + [ "${netroot%%:*}" = "nbd" ] || unset netroot +fi + +# Root takes precedence over netroot +if [ "${root%%:*}" = "nbd" ]; then + if [ -n "$netroot" ]; then + warn "root takes precedence over netroot. Ignoring netroot" + + fi + netroot=$root + unset root +fi + +# If it's not nbd we don't continue +[ "${netroot%%:*}" = "nbd" ] || return + +# Check required arguments +nroot=${netroot#nbd:} +server=${nroot%%:*} +if [ "${server%"${server#?}"}" = "[" ]; then + server=${nroot#[} + server=${server%%]:*}\] + nroot=${nroot#*]:} +else + nroot=${nroot#*:} +fi +port=${nroot%%:*} +unset nroot + +[ -z "$server" ] && die "Argument server for nbdroot is missing" +[ -z "$port" ] && die "Argument port for nbdroot is missing" + +# NBD actually supported? +incol2 /proc/devices nbd || modprobe nbd || die "nbdroot requested but kernel/initrd does not support nbd" + +# Done, all good! +# shellcheck disable=SC2034 +rootok=1 + +# Shut up init error check +if [ -z "$root" ]; then + root=block:/dev/root + # the device is created and waited for in ./nbdroot.sh +fi + +echo 'nbd-client -check /dev/nbd0 > /dev/null 2>&1' > "$hookdir"/initqueue/finished/nbdroot.sh diff --git a/modules.d/95nfs/module-setup.sh b/modules.d/95nfs/module-setup.sh new file mode 100755 index 0000000..16bafe3 --- /dev/null +++ b/modules.d/95nfs/module-setup.sh @@ -0,0 +1,138 @@ +#!/bin/bash + +# return value: +# 'nfs4': Only nfs4 founded +# 'nfs': nfs with version < 4 founded +# '': No nfs founded +get_nfs_type() { + local _nfs _nfs4 + + for fs in "${host_fs_types[@]}"; do + [[ $fs == "nfs" ]] && _nfs=1 + [[ $fs == "nfs3" ]] && _nfs=1 + [[ $fs == "nfs4" ]] && _nfs4=1 + done + + [[ "$_nfs" ]] && echo "nfs" && return + [[ "$_nfs4" ]] && echo "nfs4" && return +} + +# called by dracut +check() { + # If our prerequisites are not met, fail anyways. + require_any_binary rpcbind portmap || return 1 + require_binaries rpc.statd mount.nfs mount.nfs4 umount sed chmod chown || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + [[ "$(get_nfs_type)" ]] && return 0 + return 255 + } + return 0 +} + +# called by dracut +depends() { + # We depend on network modules being loaded + echo network +} + +# called by dracut +installkernel() { + hostonly=$(optional_hostonly) instmods '=net/sunrpc' '=fs/nfs' ipv6 nfs_acl nfs_layout_nfsv41_files +} + +cmdline() { + local nfs_device + local nfs_options + local nfs_root + local nfs_address + local lookup + + ### nfsroot= ### + nfs_device=$(findmnt -t nfs4 -n -o SOURCE /) + if [ -n "$nfs_device" ]; then + nfs_root="root=nfs4:$nfs_device" + else + nfs_device=$(findmnt -t nfs -n -o SOURCE /) + [ -z "$nfs_device" ] && return + nfs_root="root=nfs:$nfs_device" + fi + nfs_options=$(findmnt -t nfs4,nfs -n -o OPTIONS /) + [ -n "$nfs_options" ] && nfs_root="$nfs_root:$nfs_options" + echo "$nfs_root" + + ### ip= ### + if [[ $nfs_device =~ [0-9]*\.[0-9]*\.[0-9]*.[0-9]* ]] || [[ $nfs_device =~ \[[^]]*\] ]]; then + nfs_address="${nfs_device%%:*}" + else + lookup=$(host "${nfs_device%%:*}" | grep " address " | head -n1) + nfs_address=${lookup##* } + fi + + [[ $nfs_address ]] || return + ip_params_for_remote_addr "$nfs_address" +} + +# called by dracut +install() { + local _nsslibs + inst_multiple -o rpc.idmapd mount.nfs mount.nfs4 umount sed /etc/netconfig chmod chown "$tmpfilesdir/rpcbind.conf" + inst_multiple -o /etc/idmapd.conf + inst_multiple -o /etc/services /etc/nsswitch.conf /etc/rpc /etc/protocols + inst_multiple -o /usr/etc/services /usr/etc/nsswitch.conf /usr/etc/rpc /usr/etc/protocols + + if [[ $hostonly_cmdline == "yes" ]]; then + local _netconf + _netconf="$(cmdline)" + [[ $_netconf ]] && printf "%s\n" "$_netconf" >> "${initdir}/etc/cmdline.d/95nfs.conf" + fi + + if [[ -f $dracutsysrootdir/lib/modprobe.d/nfs.conf ]]; then + inst_multiple /lib/modprobe.d/nfs.conf + else + [[ -d $initdir/etc/modprobe.d ]] || mkdir -p "$initdir"/etc/modprobe.d + echo "alias nfs4 nfs" > "$initdir"/etc/modprobe.d/nfs.conf + fi + + inst_libdir_file 'libnfsidmap_nsswitch.so*' 'libnfsidmap/*.so' 'libnfsidmap*.so*' + + _nsslibs=$( + cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \ + | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \ + | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|' + ) + _nsslibs=${_nsslibs#|} + _nsslibs=${_nsslibs%|} + + inst_libdir_file -n "$_nsslibs" 'libnss_*.so*' + + inst_hook cmdline 90 "$moddir/parse-nfsroot.sh" + inst_hook pre-udev 99 "$moddir/nfs-start-rpc.sh" + inst_hook cleanup 99 "$moddir/nfsroot-cleanup.sh" + inst "$moddir/nfsroot.sh" "/sbin/nfsroot" + + # For strict hostonly, only install rpcbind for NFS < 4 + if [[ $hostonly_mode != "strict" ]] || [[ "$(get_nfs_type)" != "nfs4" ]]; then + inst_multiple -o portmap rpcbind rpc.statd + fi + + inst "$moddir/nfs-lib.sh" "/lib/nfs-lib.sh" + mkdir -m 0755 -p "$initdir/var/lib/nfs" + mkdir -m 0755 -p "$initdir/var/lib/nfs/rpc_pipefs" + mkdir -m 0770 -p "$initdir/var/lib/rpcbind" + [ -d "/var/lib/nfs/statd/sm" ] && mkdir -m 0755 -p "$initdir/var/lib/nfs/statd/sm" + [ -d "/var/lib/nfs/sm" ] && mkdir -m 0755 -p "$initdir/var/lib/nfs/sm" + + # Rather than copy the passwd file in, just set a user for rpcbind + # We'll save the state and restart the daemon from the root anyway + grep -E '^nfsnobody:|^rpc:|^rpcuser:' "$dracutsysrootdir"/etc/passwd >> "$initdir/etc/passwd" + grep -E '^nogroup:|^rpc:|^nobody:' "$dracutsysrootdir"/etc/group >> "$initdir/etc/group" + + # rpc user needs to be able to write to this directory to save the warmstart + # file + chmod 770 "$initdir/var/lib/rpcbind" + grep -q '^rpc:' "$dracutsysrootdir"/etc/passwd \ + && grep -q '^rpc:' "$dracutsysrootdir"/etc/group + + dracut_need_initqueue +} diff --git a/modules.d/95nfs/nfs-lib.sh b/modules.d/95nfs/nfs-lib.sh new file mode 100755 index 0000000..f000671 --- /dev/null +++ b/modules.d/95nfs/nfs-lib.sh @@ -0,0 +1,157 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +. /lib/net-lib.sh + +# TODO: make these things not pollute the calling namespace + +# nfs_to_var NFSROOT [NETIF] +# use NFSROOT to set $nfs, $server, $path, and $options. +# NFSROOT is something like: nfs[4]:<server>:/<path>[:<options>|,<options>] +# NETIF is used to get information from DHCP options, if needed. +nfs_to_var() { + # Unfortunately, there's multiple styles of nfs "URL" in use, so we need + # extra functions to parse them into $nfs, $server, $path, and $options. + # FIXME: local netif=${2:-$netif}? + case "$1" in + nfs://*) rfc2224_nfs_to_var "$1" ;; + nfs:*[*) anaconda_nfsv6_to_var "$1" ;; + nfs:*:*:/*) anaconda_nfs_to_var "$1" ;; + *) nfsroot_to_var "$1" ;; + esac + # if anything's missing, try to fill it in from DHCP options + if [ -z "$server" ] || [ -z "$path" ]; then nfsroot_from_dhcp "$2"; fi + # if there's a "%s" in the path, replace it with the hostname/IP + if strstr "$path" "%s"; then + local node="" + read -r node < /proc/sys/kernel/hostname + [ "$node" = "(none)" ] && node=$(get_ip "$2") + path=${path%%%s*}$node${path#*%s} # replace only the first %s + fi +} + +# root=nfs:[<server-ip>:]<root-dir>[:<nfs-options>] +# root=nfs4:[<server-ip>:]<root-dir>[:<nfs-options>] +nfsroot_to_var() { + # strip nfs[4]: + local arg="$*:" + nfs="${arg%%:*}" + arg="${arg##"$nfs":}" + + # check if we have a server + if strstr "$arg" ':/'; then + server="${arg%%:/*}" + arg="/${arg##*:/}" + fi + + path="${arg%%:*}" + + # rest are options + options="${arg##"$path"}" + # strip leading ":" + options="${options##:}" + # strip ":" + options="${options%%:}" + + # Does it really start with '/'? + [ -n "${path%%/*}" ] && path="error" + + #Fix kernel legacy style separating path and options with ',' + if [ "$path" != "${path#*,}" ]; then + options=${path#*,} + path=${path%%,*} + fi +} + +# RFC2224: nfs://<server>[:<port>]/<path> +rfc2224_nfs_to_var() { + nfs="nfs" + server="${1#nfs://}" + path="/${server#*/}" + server="${server%%/*}" + server="${server%%:}" # anaconda compat (nfs://<server>:/<path>) + local port="${server##*:}" + [ "$port" != "$server" ] && options="port=$port" +} + +# Anaconda-style path with options: nfs:<options>:<server>:/<path> +# (without mount options, anaconda is the same as dracut) +anaconda_nfs_to_var() { + nfs="nfs" + options="${1#nfs:}" + server="${options#*:}" + server="${server%:/*}" + options="${options%%:*}" + path="/${1##*:/}" +} + +# IPv6 nfs path will be treated separately +anaconda_nfsv6_to_var() { + nfs="nfs" + path="$1" + options="${path#*:/}" + path="/${options%%:*}" + server="${1#*nfs:}" + if str_starts "$server" '['; then + server="${server%:/*}" + options="${options#*:*}" + else + server="${server%:/*}" + options="${server%%:*}" + server="${server#*:}" + fi +} + +# nfsroot_from_dhcp NETIF +# fill in missing server/path from DHCP options. +nfsroot_from_dhcp() { + local f + for f in /tmp/net.$1.override /tmp/dhclient.$1.dhcpopts; do + # shellcheck disable=SC1090 + [ -f "$f" ] && . "$f" + done + [ -n "$new_root_path" ] && nfsroot_to_var "$nfs:$new_root_path" + [ -z "$path" ] && [ "$(getarg root=)" = "/dev/nfs" ] && path=/tftpboot/%s + [ -z "$server" ] && server=$srv + [ -z "$server" ] && server=$new_next_server + [ -z "$server" ] && server=$new_dhcp_server_identifier + [ -z "$server" ] && server=${new_root_path%%:*} +} + +# Look through $options, fix "rw"/"ro", move "lock"/"nolock" to $nfslock +munge_nfs_options() { + local f="" flags="" nfsrw="ro" OLDIFS="$IFS" + IFS=, + for f in $options; do + case $f in + ro | rw) nfsrw=$f ;; + lock | nolock) nfslock=$f ;; + *) flags=${flags:+$flags,}$f ;; + esac + done + IFS="$OLDIFS" + + # Override rw/ro if set on cmdline + getarg ro > /dev/null && nfsrw=ro + getarg rw > /dev/null && nfsrw=rw + + options=$nfsrw${flags:+,$flags} +} + +# mount_nfs NFSROOT MNTDIR [NETIF] +mount_nfs() { + local nfsroot="$1" mntdir="$2" netif="$3" + local nfs="" server="" path="" options="" + nfs_to_var "$nfsroot" "$netif" + munge_nfs_options + if [ "$nfs" = "nfs4" ]; then + options=$options${nfslock:+,$nfslock} + else + # NFSv{2,3} doesn't support using locks as it requires a helper to + # transfer the rpcbind state to the new root + [ "$nfslock" = "lock" ] \ + && warn "Locks unsupported on NFSv{2,3}, using nolock" 1>&2 + options=$options,nolock + fi + mount -t "$nfs" -o"$options" "$server:$path" "$mntdir" +} diff --git a/modules.d/95nfs/nfs-start-rpc.sh b/modules.d/95nfs/nfs-start-rpc.sh new file mode 100755 index 0000000..52f6a4d --- /dev/null +++ b/modules.d/95nfs/nfs-start-rpc.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +if load_fstype sunrpc rpc_pipefs; then + [ ! -d /var/lib/nfs/rpc_pipefs/nfs ] \ + && mount -t rpc_pipefs rpc_pipefs /var/lib/nfs/rpc_pipefs + + # Start rpcbind or rpcbind + # FIXME occasionally saw 'rpcbind: fork failed: No such device' -- why? + command -v portmap > /dev/null && [ -z "$(pidof portmap)" ] && portmap + if command -v rpcbind > /dev/null && [ -z "$(pidof rpcbind)" ]; then + mkdir -p /run/rpcbind + chown rpc:rpc /run/rpcbind + rpcbind + fi + + # Start rpc.statd as mount won't let us use locks on a NFSv4 + # filesystem without talking to it. NFSv4 does locks internally, + # rpc.lockd isn't needed + command -v rpc.statd > /dev/null && [ -z "$(pidof rpc.statd)" ] && rpc.statd + command -v rpc.idmapd > /dev/null && [ -z "$(pidof rpc.idmapd)" ] && rpc.idmapd +else + warn 'Kernel module "sunrpc" not in the initramfs, or support for filesystem "rpc_pipefs" missing!' +fi diff --git a/modules.d/95nfs/nfsroot-cleanup.sh b/modules.d/95nfs/nfsroot-cleanup.sh new file mode 100755 index 0000000..d99519b --- /dev/null +++ b/modules.d/95nfs/nfsroot-cleanup.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +type incol2 > /dev/null 2>&1 || . /lib/dracut-lib.sh + +[ -f /tmp/nfs.rpc_pipefs_path ] && read -r rpcpipefspath < /tmp/nfs.rpc_pipefs_path +[ -z "$rpcpipefspath" ] && rpcpipefspath=var/lib/nfs/rpc_pipefs + +pid=$(pidof rpc.statd) +[ -n "$pid" ] && kill "$pid" + +pid=$(pidof rpc.idmapd) +[ -n "$pid" ] && kill "$pid" + +pid=$(pidof rpcbind) +[ -n "$pid" ] && kill "$pid" + +if incol2 /proc/mounts /var/lib/nfs/rpc_pipefs; then + # try to create the destination directory + [ -d "$NEWROOT"/$rpcpipefspath ] \ + || mkdir -m 0755 -p "$NEWROOT"/$rpcpipefspath 2> /dev/null + + if [ -d "$NEWROOT"/$rpcpipefspath ]; then + # mount --move does not seem to work??? + mount --bind /var/lib/nfs/rpc_pipefs "$NEWROOT"/$rpcpipefspath + umount /var/lib/nfs/rpc_pipefs 2> /dev/null + else + umount /var/lib/nfs/rpc_pipefs 2> /dev/null + fi +fi diff --git a/modules.d/95nfs/nfsroot.sh b/modules.d/95nfs/nfsroot.sh new file mode 100755 index 0000000..794e0d8 --- /dev/null +++ b/modules.d/95nfs/nfsroot.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +. /lib/nfs-lib.sh + +[ "$#" = 3 ] || exit 1 + +# root is in the form root=nfs[4]:[server:]path[:options], either from +# cmdline or dhcp root-path +netif="$1" +root="$2" +NEWROOT="$3" + +nfs_to_var "$root" "$netif" +[ -z "$server" ] && die "Required parameter 'server' is missing" + +mount_nfs "$root" "$NEWROOT" "$netif" && { + [ -e /dev/root ] || ln -s null /dev/root + [ -e /dev/nfs ] || ln -s null /dev/nfs +} + +[ -f "$NEWROOT"/etc/fstab ] && cat "$NEWROOT"/etc/fstab > /dev/null + +# inject new exit_if_exists +# shellcheck disable=SC2016 +echo 'settle_exit_if_exists="--exit-if-exists=/dev/root"; rm -- "$job"' > "$hookdir"/initqueue/nfs.sh +# force udevsettle to break +: > "$hookdir"/initqueue/work + +need_shutdown diff --git a/modules.d/95nfs/parse-nfsroot.sh b/modules.d/95nfs/parse-nfsroot.sh new file mode 100755 index 0000000..0c8dbbb --- /dev/null +++ b/modules.d/95nfs/parse-nfsroot.sh @@ -0,0 +1,128 @@ +#!/bin/sh +# +# Preferred format: +# root=nfs[4]:[server:]path[:options] +# +# This syntax can come from DHCP root-path as well. +# +# Legacy format: +# root=/dev/nfs nfsroot=[server:]path[,options] +# +# In Legacy root=/dev/nfs mode, if the 'nfsroot' parameter is not given +# on the command line or is empty, the dhcp root-path is used as +# [server:]path[:options] or the default "/tftpboot/%s" will be used. +# +# If server is unspecified it will be pulled from one of the following +# sources, in order: +# static ip= option on kernel command line +# DHCP next-server option +# DHCP server-id option +# DHCP root-path option +# +# NFSv4 is only used if explicitly requested with nfs4: prefix, otherwise +# NFSv3 is used. +# + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +. /lib/nfs-lib.sh + +# This script is sourced, so root should be set. But let's be paranoid +[ -z "$root" ] && root=$(getarg root=) +[ -z "$nfsroot" ] && nfsroot=$(getarg nfsroot=) + +[ -n "$netroot" ] && oldnetroot="$netroot" + +# netroot= cmdline argument must be ignored, but must be used if +# we're inside netroot to parse dhcp root-path +if [ -n "$netroot" ]; then + for n in $(getargs netroot=); do + [ "$n" = "$netroot" ] && break + done + if [ "$n" = "$netroot" ]; then + #warn "Ignoring netroot argument for NFS" + netroot=$root + fi +else + netroot=$root +fi + +# LEGACY: nfsroot= is valid only if root=/dev/nfs +if [ -n "$nfsroot" ]; then + # @deprecated + warn "Argument nfsroot is deprecated and might be removed in a future release. See 'man dracut.kernel' for more information." + if [ "$(getarg root=)" != "/dev/nfs" ]; then + die "Argument nfsroot only accepted for legacy root=/dev/nfs" + fi + netroot=nfs:$nfsroot +fi + +case "$netroot" in + /dev/nfs) netroot=nfs ;; + /dev/*) + if [ -n "$oldnetroot" ]; then + netroot="$oldnetroot" + else + unset netroot + fi + return + ;; + # LEGACY: root=<server-ip>:/<path + [0-9]*:/* | [0-9]*\.[0-9]*\.[0-9]*[!:] | /*) + netroot=nfs:$netroot + ;; +esac + +# Continue if nfs +case "${netroot%%:*}" in + nfs | nfs4 | /dev/nfs) ;; + *) + if [ -n "$oldnetroot" ]; then + netroot="$oldnetroot" + else + unset netroot + fi + return + ;; +esac + +# Check required arguments + +if nfsdomain=$(getarg rd.nfs.domain -d rd_NFS_DOMAIN); then + if [ -f /etc/idmapd.conf ]; then + sed -i -e \ + "s/^[[:space:]#]*Domain[[:space:]]*=.*/Domain = $nfsdomain/g" \ + /etc/idmapd.conf + fi + # and even again after the sed, in case it was not yet specified + echo "Domain = $nfsdomain" >> /etc/idmapd.conf +fi + +nfsroot_to_var "$netroot" +[ "$path" = "error" ] && die "Argument nfsroot must contain a valid path!" + +# Set fstype, might help somewhere +fstype=${nfs#/dev/} + +# Rewrite root so we don't have to parse this ugliness later on again +netroot="$fstype:$server:$path:$options" + +# If we don't have a server, we need dhcp +if [ -z "$server" ]; then + # shellcheck disable=SC2034 + DHCPORSERVER="1" +fi + +# Done, all good! +# shellcheck disable=SC2034 +rootok=1 + +# Shut up init error check or make sure that block parser won't get +# confused by having /dev/nfs[4] +root="$fstype" + +# shellcheck disable=SC2016 +echo '[ -e $NEWROOT/proc ]' > "$hookdir"/initqueue/finished/nfsroot.sh + +mkdir -p /var/lib/rpcbind +chown rpc:rpc /var/lib/rpcbind +chmod 770 /var/lib/rpcbind diff --git a/modules.d/95nvmf/95-nvmf-initqueue.rules b/modules.d/95nvmf/95-nvmf-initqueue.rules new file mode 100644 index 0000000..d26d7b0 --- /dev/null +++ b/modules.d/95nvmf/95-nvmf-initqueue.rules @@ -0,0 +1,10 @@ +# +# nvmf-initqueue.rules +# +# D-Bus doesn't run in the initrd, which means that we cannot use our +# usual trick of starting custom systemd services. +# So use a rule to create initqueue entries instead. + +ACTION=="change", SUBSYSTEM=="fc", ENV{FC_EVENT}=="nvmediscovery", \ + ENV{NVMEFC_HOST_TRADDR}=="*", ENV{NVMEFC_TRADDR}=="*", \ + RUN+="/sbin/initqueue --onetime --unique --name nvmf-connect-$env{NVMEFC_TRADDR}-$env{NVMEFC_HOST_TRADDR} /usr/sbin/nvme connect-all --transport=fc --traddr=$env{NVMEFC_TRADDR} --host-traddr=$env{NVMEFC_HOST_TRADDR}" diff --git a/modules.d/95nvmf/module-setup.sh b/modules.d/95nvmf/module-setup.sh new file mode 100755 index 0000000..a8f3034 --- /dev/null +++ b/modules.d/95nvmf/module-setup.sh @@ -0,0 +1,148 @@ +#!/bin/bash + +# called by dracut +check() { + require_binaries nvme jq || return 1 + [ -f /etc/nvme/hostnqn ] || return 255 + [ -f /etc/nvme/hostid ] || return 255 + + is_nvmf() { + local _dev=$1 + local trtype + + [[ -L "/sys/dev/block/$_dev" ]] || return 0 + cd -P "/sys/dev/block/$_dev" || return 0 + if [ -f partition ]; then + cd .. + fi + for d in device/nvme*; do + [ -L "$d" ] || continue + if readlink "$d" | grep -q nvme-fabrics; then + read -r trtype < "$d"/transport + break + fi + done + [[ $trtype == "fc" ]] || [[ $trtype == "tcp" ]] || [[ $trtype == "rdma" ]] + } + + has_nbft() { + local f found= + for f in /sys/firmware/acpi/tables/NBFT*; do + [ -f "$f" ] || continue + found=1 + break + done + [[ $found ]] + } + + [[ $hostonly ]] || [[ $mount_needs ]] && { + pushd . > /dev/null + for_each_host_dev_and_slaves is_nvmf + local _is_nvmf=$? + popd > /dev/null || exit + [[ $_is_nvmf == 0 ]] || return 255 + if [ ! -f /sys/class/fc/fc_udev_device/nvme_discovery ] \ + && [ ! -f /etc/nvme/discovery.conf ] \ + && [ ! -f /etc/nvme/config.json ] && ! has_nbft; then + echo "No discovery arguments present" + return 255 + fi + } + return 0 +} + +# called by dracut +depends() { + echo bash rootfs-block network + return 0 +} + +# called by dracut +installkernel() { + instmods nvme_fc lpfc qla2xxx + hostonly="" instmods nvme_tcp nvme_fabrics 8021q +} + +# called by dracut +cmdline() { + local _hostnqn + local _hostid + + gen_nvmf_cmdline() { + local _dev=$1 + local trtype + local traddr + local host_traddr + local trsvcid + local _address + local -a _address_parts + + [[ -L "/sys/dev/block/$_dev" ]] || return 0 + cd -P "/sys/dev/block/$_dev" || return 0 + if [ -f partition ]; then + cd .. + fi + for d in device/nvme*; do + [ -L "$d" ] || continue + if readlink "$d" | grep -q nvme-fabrics; then + read -r trtype < "$d"/transport + break + fi + done + + [ -z "$trtype" ] && return 0 + nvme list-subsys "${PWD##*/}" | while read -r _ _ trtype _address _; do + [[ -z $trtype || $trtype != "${trtype#NQN}" ]] && continue + unset traddr + unset host_traddr + unset trsvcid + mapfile -t -d ',' _address_parts < <(printf "%s" "$_address") + for i in "${_address_parts[@]}"; do + [[ $i =~ ^traddr= ]] && traddr="${i#traddr=}" + [[ $i =~ ^host_traddr= ]] && host_traddr="${i#host_traddr=}" + [[ $i =~ ^trsvcid= ]] && trsvcid="${i#trsvcid=}" + done + [[ -z $traddr && -z $host_traddr && -z $trsvcid ]] && continue + echo -n " rd.nvmf.discover=$trtype,$traddr,$host_traddr,$trsvcid" + done + } + + if [ -f /etc/nvme/hostnqn ]; then + read -r _hostnqn < /etc/nvme/hostnqn + echo -n " rd.nvmf.hostnqn=${_hostnqn}" + fi + if [ -f /etc/nvme/hostid ]; then + read -r _hostid < /etc/nvme/hostid + echo -n " rd.nvmf.hostid=${_hostid}" + fi + + [[ $hostonly ]] || [[ $mount_needs ]] && { + pushd . > /dev/null + for_each_host_dev_and_slaves gen_nvmf_cmdline + popd > /dev/null || exit + } +} + +# called by dracut +install() { + if [[ $hostonly_cmdline == "yes" ]]; then + local _nvmf_args + _nvmf_args=$(cmdline) + [[ "$_nvmf_args" ]] && printf "%s" "$_nvmf_args" >> "${initdir}/etc/cmdline.d/95nvmf-args.conf" + fi + inst_simple "/etc/nvme/hostnqn" + inst_simple "/etc/nvme/hostid" + + inst_multiple ip sed + + inst_script "${moddir}/nvmf-autoconnect.sh" /sbin/nvmf-autoconnect.sh + inst_script "${moddir}/nbftroot.sh" /sbin/nbftroot + + inst_multiple nvme jq + inst_hook cmdline 92 "$moddir/parse-nvmf-boot-connections.sh" + inst_simple "/etc/nvme/discovery.conf" + inst_simple "/etc/nvme/config.json" + inst_rules /usr/lib/udev/rules.d/71-nvmf-iopolicy-netapp.rules + inst_rules "$moddir/95-nvmf-initqueue.rules" + dracut_need_initqueue +} diff --git a/modules.d/95nvmf/nbftroot.sh b/modules.d/95nvmf/nbftroot.sh new file mode 100755 index 0000000..0f33499 --- /dev/null +++ b/modules.d/95nvmf/nbftroot.sh @@ -0,0 +1,5 @@ +#! /bin/sh +# This script is called from /sbin/netroot + +/sbin/nvmf-autoconnect.sh online +exit 0 diff --git a/modules.d/95nvmf/nvmf-autoconnect.sh b/modules.d/95nvmf/nvmf-autoconnect.sh new file mode 100755 index 0000000..35ee948 --- /dev/null +++ b/modules.d/95nvmf/nvmf-autoconnect.sh @@ -0,0 +1,54 @@ +#!/bin/sh +# Argument $1 is "settled", "online", or "timeout", indicating +# the queue from which the script is called. +# In the "timeout" case, try everything. +# Otherwise, try options according to the priorities below. + +[ "$RD_DEBUG" != yes ] || set -x + +if [ "$1" = timeout ]; then + [ ! -f /sys/class/fc/fc_udev_device/nvme_discovery ] \ + || echo add > /sys/class/fc/fc_udev_device/nvme_discovery + /usr/sbin/nvme connect-all + exit 0 +fi + +NVMF_HOSTNQN_OK= +[ ! -f "/etc/nvme/hostnqn" ] || [ ! -f "/etc/nvme/hostid" ] || NVMF_HOSTNQN_OK=1 + +# Only nvme-cli 2.5 or newer supports the options --nbft and --no-nbft +# for the connect-all command. +# Make sure we don't use unsupported options with earlier versions. +NBFT_SUPPORTED= +# shellcheck disable=SC2016 +/usr/sbin/nvme connect-all --help 2>&1 | sed -n '/[[:space:]]--nbft[[:space:]]/q1;$q0' \ + || NBFT_SUPPORTED=1 + +if [ -e /tmp/nvmf-fc-auto ] && [ "$NVMF_HOSTNQN_OK" ] \ + && [ -f /sys/class/fc/fc_udev_device/nvme_discovery ]; then + # prio 1: cmdline override "rd.nvmf.discovery=fc,auto" + echo add > /sys/class/fc/fc_udev_device/nvme_discovery + exit 0 +fi +if [ "$NBFT_SUPPORTED" ] && [ -e /tmp/valid_nbft_entry_found ]; then + # prio 2: NBFT + /usr/sbin/nvme connect-all --nbft + exit 0 +fi +if [ -f /etc/nvme/discovery.conf ] || [ -f /etc/nvme/config.json ] \ + && [ "$NVMF_HOSTNQN_OK" ]; then + # prio 3: configuration from initrd and/or kernel command line + # We can get here even if "rd.nvmf.nonbft" was given, thus use --no-nbft + if [ "$NBFT_SUPPORTED" ]; then + /usr/sbin/nvme connect-all --no-nbft + else + /usr/sbin/nvme connect-all + fi + exit 0 +fi +if [ "$NVMF_HOSTNQN_OK" ] \ + && [ -f /sys/class/fc/fc_udev_device/nvme_discovery ]; then + # prio 4: no discovery entries, try NVMeoFC autoconnect + echo add > /sys/class/fc/fc_udev_device/nvme_discovery +fi +exit 0 diff --git a/modules.d/95nvmf/parse-nvmf-boot-connections.sh b/modules.d/95nvmf/parse-nvmf-boot-connections.sh new file mode 100755 index 0000000..6601837 --- /dev/null +++ b/modules.d/95nvmf/parse-nvmf-boot-connections.sh @@ -0,0 +1,326 @@ +#!/bin/sh +# +# Supported formats: +# rd.nvmf.hostnqn=<hostnqn> +# rd.nvmf.hostid=<hostid> +# rd.nvmf.discover=<transport>,<traddr>,<host-traddr>,<trsvcid> +# +# Examples: +# rd.nvmf.hostnqn=nqn.2014-08.org.nvmexpress:uuid:37303738-3034-584d-5137-333230423843 +# rd.nvmf.discover=rdma,192.168.1.3,,4420 +# rd.nvmf.discover=tcp,192.168.1.3,,4420 +# rd.nvmf.discover=tcp,192.168.1.3 +# rd.nvmf.discover=fc,nn-0x200400a098d85236:pn-0x201400a098d85236,nn-0x200000109b7db455:pn-0x100000109b7db455 +# rd.nvmf.discover=fc,auto +# +# Note: FC does autodiscovery, so typically there is no need to +# specify any discover parameters for FC. +# + +command -v getarg > /dev/null || . /lib/dracut-lib.sh +command -v is_ip > /dev/null || . /lib/net-lib.sh + +## Sample NBFT output from nvme show-nbft -H -s -d -o json +# [ +# { +# "filename":"/sys/firmware/acpi/tables/NBFT", +# "host":{ +# "nqn":"nqn.2014-08.org.nvmexpress:uuid:d6f07002-7eb5-4841-a185-400e296afae4", +# "id":"111919da-21ea-cc4e-bafe-216d8372dd31", +# "host_id_configured":0, +# "host_nqn_configured":0, +# "primary_admin_host_flag":"not indicated" +# }, +# "subsystem":[ +# { +# "index":1, +# "num_hfis":1, +# "hfis":[ +# 1 +# ], +# "transport":"tcp", +# "transport_address":"192.168.100.216", +# "transport_svcid":"4420", +# "subsys_port_id":0, +# "nsid":1, +# "nid_type":"uuid", +# "nid":"424d1c8a-8ef9-4681-b2fc-8c343bd8fa69", +# "subsys_nqn":"timberland-01", +# "controller_id":0, +# "asqsz":0, +# "pdu_header_digest_required":0, +# "data_digest_required":0 +# } +# ], +# "hfi":[ +# { +# "index":1, +# "transport":"tcp", +# "pcidev":"0:0:2.0", +# "mac_addr":"52:54:00:4f:97:e9", +# "vlan":0, +# "ip_origin":63, +# "ipaddr":"192.168.100.217", +# "subnet_mask_prefix":24, +# "gateway_ipaddr":"0.0.0.0", +# "route_metric":0, +# "primary_dns_ipaddr":"0.0.0.0", +# "secondary_dns_ipaddr":"0.0.0.0", +# "dhcp_server_ipaddr":"", +# "this_hfi_is_default_route":1 +# } +# ], +# "discovery":[ +# ] +# } +# ] +# +# If the IP address is derived from DHCP, it sets the field +# "hfi.dhcp_server_ipaddr" to a non-emtpy value. +# +# + +nbft_run_jq() { + local st + local opts="-e" + + while [ $# -gt 0 ]; do + case $1 in + -*) + opts="$opts $1" + ;; + *) + break + ;; + esac + shift + done + # Not quoting is intentional here. We won't get glob expressions passed. + # shellcheck disable=SC2086 + jq $opts "$1" << EOF +$2 +EOF + st=$? + if [ $st -ne 0 ]; then + warn "NBFT: jq error while processing \"$1\"" + return $st + else + return 0 + fi +} + +nbft_check_empty_address() { + # suppress meaningless or empty IP addresses + # "null" is returned by jq if no match found for expression + case $1 in + null | "::" | "0.0.0.0") ;; + *) + echo "$1" + ;; + esac +} + +nbft_parse_hfi() { + # false positive of shellcheck - no expansion in variable assignments + # shellcheck disable=2086 + local hfi_json=$1 + local mac iface ipaddr prefix vlan gateway dns1 dns2 hostname adrfam dhcp + + mac=$(nbft_run_jq -r .mac_addr "$hfi_json") || return 1 + iface=$(set_ifname nbft "$mac") + + vlan=$(nbft_run_jq .vlan "$hfi_json") || vlan=0 + # treat VLAN zero as "no vlan" + [ "$vlan" -ne 0 ] || vlan= + + [ ! -e /tmp/net."${iface}${vlan:+.$vlan}".has_ibft_config ] || return 0 + + dhcp=$(nbft_run_jq -r .dhcp_server_ipaddr "$hfi_json") + # We need to check $? here as the above is an assignment + # shellcheck disable=2181 + if [ $? -eq 0 ] && [ "$dhcp" ] && [ "$dhcp" != null ]; then + case $dhcp in + *:*) + echo ip="$iface${vlan:+.$vlan}:dhcp6" + ;; + *.*.*.*) + echo ip="$iface${vlan:+.$vlan}:dhcp" + ;; + *) + warn "Invalid value for dhcp_server_ipaddr: $dhcp" + return 1 + ;; + esac + else + ipaddr=$(nbft_run_jq -r .ipaddr "$hfi_json") || return 1 + + case $ipaddr in + *.*.*.*) + adrfam=ipv4 + ;; + *:*) + adrfam=ipv6 + ;; + *) + warn "invalid address: $ipaddr" + return 1 + ;; + esac + prefix=$(nbft_run_jq -r .subnet_mask_prefix "$hfi_json") + # Need to check $? here as he above is an assignment + # shellcheck disable=2181 + if [ $? -ne 0 ] && [ "$adrfam" = ipv6 ]; then + prefix=128 + fi + # Use brackets for IPv6 + if [ "$adrfam" = ipv6 ]; then + ipaddr="[$ipaddr]" + fi + + gateway=$(nbft_check_empty_address \ + "$(nbft_run_jq -r .gateway_ipaddr "$hfi_json")") + dns1=$(nbft_check_empty_address \ + "$(nbft_run_jq -r .primary_dns_ipaddr "$hfi_json")") + dns2=$(nbft_check_empty_address \ + "$(nbft_run_jq -r .secondary_dns_ipaddr "$hfi_json")") + hostname=$(nbft_run_jq -r .host_name "$hfi_json" 2> /dev/null) || hostname= + + echo "ip=$ipaddr::$gateway:$prefix:$hostname:$iface${vlan:+.$vlan}:none${dns1:+:$dns1}${dns2:+:$dns2}" + fi + + if [ "$vlan" ]; then + echo "vlan=$iface.$vlan:$iface" + echo "$mac" > "/tmp/net.$iface.$vlan.has_ibft_config" + else + echo "$mac" > "/tmp/net.$iface.has_ibft_config" + fi + : > /tmp/valid_nbft_entry_found +} + +nbft_parse() { + local nbft_json n_nbft all_hfi_json n_hfi + local j=0 i + + nbft_json=$(nvme nbft show -H -o json) || return 0 + n_nbft=$(nbft_run_jq ". | length" "$nbft_json") || return 0 + + while [ "$j" -lt "$n_nbft" ]; do + all_hfi_json=$(nbft_run_jq ".[$j].hfi" "$nbft_json") || continue + n_hfi=$(nbft_run_jq ". | length" "$all_hfi_json") || continue + i=0 + + while [ "$i" -lt "$n_hfi" ]; do + nbft_parse_hfi "$(nbft_run_jq ".[$i]" "$all_hfi_json")" + i=$((i + 1)) + done + j=$((j + 1)) + done >> /etc/cmdline.d/40-nbft.conf +} + +if getargbool 0 rd.nonvmf; then + warn "rd.nonvmf=0: skipping nvmf" + return 0 +fi + +if getargbool 0 rd.nvmf.nostatic; then + rm -f /etc/cmdline.d/95nvmf-args.conf + rm -f /etc/nvme/discovery.conf /etc/nvme/config.json +fi + +if ! getargbool 0 rd.nvmf.nonbft; then + for _x in /sys/firmware/acpi/tables/NBFT*; do + if [ -f "$_x" ]; then + nbft_parse + break + fi + done +fi + +initqueue --onetime modprobe --all -b -q nvme_tcp nvme_core nvme_fabrics + +parse_nvmf_discover() { + traddr="none" + trtype="none" + hosttraddr="none" + trsvcid=4420 + OLDIFS="$IFS" + IFS=, + # shellcheck disable=SC2086 + set -- $1 + IFS="$OLDIFS" + + case $# in + 2) + [ -n "$1" ] && trtype=$1 + [ -n "$2" ] && traddr=$2 + ;; + 3) + [ -n "$1" ] && trtype=$1 + [ -n "$2" ] && traddr=$2 + [ -n "$3" ] && hosttraddr=$3 + ;; + 4) + [ -n "$1" ] && trtype=$1 + [ -n "$2" ] && traddr=$2 + [ -n "$3" ] && hosttraddr=$3 + [ -n "$4" ] && trsvcid=$4 + ;; + *) + warn "Invalid arguments for rd.nvmf.discover=$1" + return 0 + ;; + esac + if [ "$traddr" = "none" ]; then + warn "traddr is mandatory for $trtype" + return 0 + fi + if [ "$trtype" = "tcp" ]; then + : > /tmp/nvmf_needs_network + elif [ "$trtype" = "fc" ]; then + if [ "$traddr" = "auto" ]; then + rm -f /etc/nvme/discovery.conf /etc/nvme/config.json + return 1 + fi + if [ "$hosttraddr" = "none" ]; then + warn "host traddr is mandatory for fc" + return 0 + fi + elif [ "$trtype" != "rdma" ]; then + warn "unsupported transport $trtype" + return 0 + fi + if [ "$trtype" = "fc" ]; then + echo "--transport=$trtype --traddr=$traddr --host-traddr=$hosttraddr" >> /etc/nvme/discovery.conf + else + echo "--transport=$trtype --traddr=$traddr --host-traddr=$hosttraddr --trsvcid=$trsvcid" >> /etc/nvme/discovery.conf + fi + return 0 +} + +nvmf_hostnqn=$(getarg rd.nvmf.hostnqn -d nvmf.hostnqn=) +if [ -n "$nvmf_hostnqn" ]; then + echo "$nvmf_hostnqn" > /etc/nvme/hostnqn +fi +nvmf_hostid=$(getarg rd.nvmf.hostid -d nvmf.hostid=) +if [ -n "$nvmf_hostid" ]; then + echo "$nvmf_hostid" > /etc/nvme/hostid +fi + +rm -f /tmp/nvmf-fc-auto +for d in $(getargs rd.nvmf.discover -d nvmf.discover=); do + parse_nvmf_discover "$d" || { + : > /tmp/nvmf-fc-auto + break + } +done + +if [ -e /tmp/nvmf_needs_network ] || [ -e /tmp/valid_nbft_entry_found ]; then + echo "rd.neednet=1" > /etc/cmdline.d/nvmf-neednet.conf + # netroot is a global variable that is present in all "sourced" scripts + # shellcheck disable=SC2034 + netroot=nbft + rm -f /tmp/nvmf_needs_network +fi + +/sbin/initqueue --settled --onetime --name nvmf-connect-settled /sbin/nvmf-autoconnect.sh settled +/sbin/initqueue --timeout --onetime --name nvmf-connect-timeout /sbin/nvmf-autoconnect.sh timeout diff --git a/modules.d/95qeth_rules/module-setup.sh b/modules.d/95qeth_rules/module-setup.sh new file mode 100755 index 0000000..a84ac15 --- /dev/null +++ b/modules.d/95qeth_rules/module-setup.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# called by dracut +check() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + local _online=0 + [ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1 + dracut_module_included network || return 1 + + [[ $hostonly ]] && { + for i in /sys/devices/qeth/*/online; do + [ ! -f "$i" ] && continue + read -r _online < "$i" + [ "$_online" -eq 1 ] && return 0 + done + } + return 255 +} + +# called by dracut +installkernel() { + instmods qeth +} + +# called by dracut +install() { + ccwid() { + qeth_path=$(readlink -e -q "$1"/device) + basename "$qeth_path" + } + + inst_rules_qeth() { + for rule in /etc/udev/rules.d/{4,5}1-qeth-${1}.rules; do + # prefer chzdev generated 41- rules + if [ -f "$rule" ]; then + inst_rules "$rule" + break + fi + done + } + + has_carrier() { + carrier=0 + # not readable in qeth interfaces + # that have just been assembled, ignore + # read error and assume no carrier + read -r carrier 2> /dev/null < "$1/carrier" + [ "$carrier" -eq 1 ] && return 0 + return 1 + } + + for dev in /sys/class/net/*; do + has_carrier "$dev" || continue + id=$(ccwid "$dev") + [ -n "$id" ] && inst_rules_qeth "$id" + done + +} diff --git a/modules.d/95resume/module-setup.sh b/modules.d/95resume/module-setup.sh new file mode 100755 index 0000000..d255103 --- /dev/null +++ b/modules.d/95resume/module-setup.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# called by dracut +check() { + swap_on_netdevice() { + local _dev + for _dev in "${swap_devs[@]}"; do + block_is_netdevice "$(get_maj_min "$_dev")" && return 0 + done + return 1 + } + + # Only support resume if hibernation is currently on + # and no swap is mounted on a net device + [[ $hostonly ]] || [[ $mount_needs ]] && { + swap_on_netdevice || [[ -f /sys/power/resume && "$(< /sys/power/resume)" == "0:0" ]] || grep -rq '^\|[[:space:]]resume=' /proc/cmdline /etc/cmdline /etc/cmdline.d /etc/kernel/cmdline /usr/lib/kernel/cmdline 2> /dev/null && return 255 + } + + return 0 +} + +# called by dracut +cmdline() { + local _resume + + for dev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$dev]} =~ ^(swap|swsuspend|swsupend)$ ]] || continue + _resume=$(shorten_persistent_dev "$(get_persistent_dev "$dev")") + [[ -n ${_resume} ]] && printf " resume=%s" "${_resume}" + done +} + +# called by dracut +install() { + local _bin + local _resumeconf + + if [[ $hostonly_cmdline == "yes" ]]; then + _resumeconf=$(cmdline) + [[ $_resumeconf ]] && printf "%s\n" "$_resumeconf" >> "${initdir}/etc/cmdline.d/95resume.conf" + fi + + # if systemd is included and has the hibernate-resume tool, use it and nothing else + if dracut_module_included "systemd" && [[ -x $dracutsysrootdir$systemdutildir/systemd-hibernate-resume ]]; then + inst_multiple -o \ + "$systemdutildir"/system-generators/systemd-hibernate-resume-generator \ + "$systemdsystemunitdir"/systemd-hibernate-resume@.service \ + "$systemdutildir"/systemd-hibernate-resume + return 0 + fi + + # Optional uswsusp support + for _bin in /usr/sbin/resume /usr/lib/suspend/resume /usr/lib64/suspend/resume /usr/lib/uswsusp/resume /usr/lib64/uswsusp/resume; do + [[ -x $dracutsysrootdir${_bin} ]] && { + inst "${_bin}" /usr/sbin/resume + [[ $hostonly ]] && [[ -f $dracutsysrootdir/etc/suspend.conf ]] && inst -H /etc/suspend.conf + break + } + done + + if ! dracut_module_included "systemd"; then + inst_hook cmdline 10 "$moddir/parse-resume.sh" + else + inst_script "$moddir/parse-resume.sh" /lib/dracut/parse-resume.sh + fi + + inst_script "$moddir/resume.sh" /lib/dracut/resume.sh +} diff --git a/modules.d/95resume/parse-resume.sh b/modules.d/95resume/parse-resume.sh new file mode 100755 index 0000000..75a905d --- /dev/null +++ b/modules.d/95resume/parse-resume.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +if resume=$(getarg resume=) && ! getarg noresume; then + export resume + echo "$resume" > /.resume +else + unset resume +fi + +resume="$(label_uuid_to_dev "$resume")" + +if splash=$(getarg splash=); then + export splash +else + unset splash +fi + +case "$splash" in + quiet) + a_splash="-P splash=y" + ;; + *) + a_splash="-P splash=n" + ;; +esac + +if ! getarg noresume; then + if [ -n "$resume" ]; then + wait_for_dev /dev/resume + + { + printf "KERNEL==\"%s\", ACTION==\"add|change\", SYMLINK+=\"resume\"\n" \ + "${resume#/dev/}" + printf "SYMLINK==\"%s\", ACTION==\"add|change\", SYMLINK+=\"resume\"\n" \ + "${resume#/dev/}" + } >> /etc/udev/rules.d/99-resume-link.rules + + { + if [ -x /usr/sbin/resume ]; then + printf -- 'KERNEL=="%s", ' "${resume#/dev/}" + printf -- '%s' 'ACTION=="add|change", ENV{ID_FS_TYPE}=="suspend|swsuspend|swsupend",' + printf -- " RUN+=\"/sbin/initqueue --finished --unique --name 00resume /usr/sbin/resume %s \'%s\'\"\n" \ + "$a_splash" "$resume" + printf -- 'SYMLINK=="%s", ' "${resume#/dev/}" + printf -- '%s' 'ACTION=="add|change", ENV{ID_FS_TYPE}=="suspend|swsuspend|swsupend",' + printf -- " RUN+=\"/sbin/initqueue --finished --unique --name 00resume /usr/sbin/resume %s \'%s\'\"\n" \ + "$a_splash" "$resume" + fi + + printf -- 'KERNEL=="%s", ' "${resume#/dev/}" + printf -- '%s' 'ACTION=="add|change", ENV{ID_FS_TYPE}=="suspend|swsuspend|swsupend",' + printf -- '%s\n' ' RUN+="/sbin/initqueue --finished --unique --name 00resume echo %M:%m > /sys/power/resume"' + + printf -- 'SYMLINK=="%s", ' "${resume#/dev/}" + printf -- '%s' 'ACTION=="add|change", ENV{ID_FS_TYPE}=="suspend|swsuspend|swsupend",' + printf -- '%s\n' ' RUN+="/sbin/initqueue --finished --unique --name 00resume echo %M:%m > /sys/power/resume"' + } >> /etc/udev/rules.d/99-resume.rules + + # shellcheck disable=SC2016 + printf '[ -e "%s" ] && { ln -fs "%s" /dev/resume 2> /dev/null; rm -f -- "$job" "%s/initqueue/timeout/resume.sh"; }\n' \ + "$resume" "$resume" "$hookdir" >> "$hookdir"/initqueue/settled/resume.sh + + { + printf -- "%s" 'warn "Cancelling resume operation. Device not found.";' + # shellcheck disable=SC2016 + printf -- ' cancel_wait_for_dev /dev/resume; rm -f -- "$job" "%s/initqueue/settled/resume.sh";\n' "$hookdir" + } >> "$hookdir"/initqueue/timeout/resume.sh + + mv /lib/dracut/resume.sh /lib/dracut/hooks/pre-mount/10-resume.sh + else + { + if [ -x /usr/sbin/resume ]; then + printf -- '%s' 'SUBSYSTEM=="block", ACTION=="add|change", ENV{ID_FS_TYPE}=="suspend|swsuspend|swsupend",' + # shellcheck disable=SC2016 + printf -- ' RUN+="/sbin/initqueue --finished --unique --name 00resume /usr/sbin/resume %s $tempnode"\n' "$a_splash" + fi + printf -- '%s' 'SUBSYSTEM=="block", ACTION=="add|change", ENV{ID_FS_TYPE}=="suspend|swsuspend|swsupend",' + printf -- '%s\n' ' RUN+="/sbin/initqueue --finished --unique --name 00resume echo %M:%m > /sys/power/resume"' + } >> /etc/udev/rules.d/99-resume.rules + fi +fi diff --git a/modules.d/95resume/resume.sh b/modules.d/95resume/resume.sh new file mode 100755 index 0000000..c808880 --- /dev/null +++ b/modules.d/95resume/resume.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +[ -s /.resume -a -b "$resume" ] && { + # First try user level resume; it offers splash etc + case "$splash" in + quiet) + a_splash="-P splash=y" + ;; + *) + a_splash="-P splash=n" + ;; + esac + [ -x "$(command -v resume)" ] && command resume "$a_splash" "$resume" + + (readlink -fn "$resume" > /sys/power/resume) > /.resume +} diff --git a/modules.d/95rootfs-block/block-genrules.sh b/modules.d/95rootfs-block/block-genrules.sh new file mode 100755 index 0000000..d5df0ee --- /dev/null +++ b/modules.d/95rootfs-block/block-genrules.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ "${root%%:*}" = "block" ]; then + { + printf 'KERNEL=="%s", SYMLINK+="root"\n' \ + "${root#block:/dev/}" + printf 'SYMLINK=="%s", SYMLINK+="root"\n' \ + "${root#block:/dev/}" + } >> /etc/udev/rules.d/99-root.rules + + # shellcheck disable=SC2016 + printf '[ -e "%s" ] && { ln -s "%s" /dev/root 2>/dev/null; rm "$job"; }\n' \ + "${root#block:}" "${root#block:}" > "$hookdir"/initqueue/settled/blocksymlink.sh + + wait_for_dev "${root#block:}" +fi diff --git a/modules.d/95rootfs-block/module-setup.sh b/modules.d/95rootfs-block/module-setup.sh new file mode 100755 index 0000000..396fb11 --- /dev/null +++ b/modules.d/95rootfs-block/module-setup.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# called by dracut +check() { + return 0 +} + +# called by dracut +depends() { + echo base fs-lib +} + +cmdline_journal() { + if [[ $hostonly ]]; then + for dev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$dev]} == "reiserfs" ]] || [[ ${host_fs_types[$dev]} == "xfs" ]] || continue + rootopts=$(find_dev_fsopts "$dev") + if [[ ${host_fs_types[$dev]} == "reiserfs" ]]; then + journaldev=$(fs_get_option "$rootopts" "jdev") + elif [[ ${host_fs_types[$dev]} == "xfs" ]]; then + journaldev=$(fs_get_option "$rootopts" "logdev") + fi + + if [ -n "$journaldev" ]; then + printf " root.journaldev=%s" "$journaldev" + fi + done + fi + return 0 +} + +cmdline_rootfs() { + local _block + _block=$(find_root_block_device) + local _dev=/dev/block/$_block + local _fstype _flags _subvol + + # "--no-hostonly-default-device" can result in empty root_devs + if [ "${#root_devs[@]}" -eq 0 ]; then + return + fi + + if [ -n "$_block" -a -b "$_dev" ]; then + printf " root=%s" "$(shorten_persistent_dev "$(get_persistent_dev "$_dev")")" + fi + _fstype="$(find_mp_fstype /)" + if [[ ${_fstype} == "zfs" ]]; then + local _root_ds + _root_ds="$(findmnt -n -o SOURCE /)" + printf " root=zfs:%s" "${_root_ds// /+}" + fi + _flags="$(find_mp_fsopts /)" + if [ -n "$_fstype" ]; then + printf " rootfstype=%s" "$_fstype" + fi + if [[ $use_fstab != yes ]] && [[ $_fstype == btrfs ]]; then + _subvol=$(findmnt -e -v -n -o FSROOT --target /) \ + && _subvol=${_subvol#/} + _flags="$_flags${_subvol:+,subvol=$_subvol}" + fi + if [ -n "$_flags" ]; then + printf " rootflags=%s" "$_flags" + fi +} + +# called by dracut +cmdline() { + cmdline_rootfs + cmdline_journal +} + +# called by dracut +install() { + if [[ $hostonly_cmdline == "yes" ]]; then + local _journaldev + _journaldev=$(cmdline_journal) + [[ $_journaldev ]] && printf "%s\n" "$_journaldev" >> "${initdir}/etc/cmdline.d/95root-journaldev.conf" + local _rootdev + _rootdev=$(cmdline_rootfs) + [[ $_rootdev ]] && printf "%s\n" "$_rootdev" >> "${initdir}/etc/cmdline.d/95root-dev.conf" + fi + + inst_multiple umount + inst_multiple tr + if ! dracut_module_included "systemd"; then + inst_hook cmdline 95 "$moddir/parse-block.sh" + inst_hook pre-udev 30 "$moddir/block-genrules.sh" + inst_hook mount 99 "$moddir/mount-root.sh" + fi + + inst_hook initqueue/timeout 99 "$moddir/rootfallback.sh" +} diff --git a/modules.d/95rootfs-block/mount-root.sh b/modules.d/95rootfs-block/mount-root.sh new file mode 100755 index 0000000..c488b11 --- /dev/null +++ b/modules.d/95rootfs-block/mount-root.sh @@ -0,0 +1,135 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh +type det_fs > /dev/null 2>&1 || . /lib/fs-lib.sh + +mount_root() { + local _rflags_ro + # sanity - determine/fix fstype + rootfs=$(det_fs "${root#block:}" "$fstype") + + journaldev=$(getarg "root.journaldev=") + if [ -n "$journaldev" ]; then + case "$rootfs" in + xfs) + rflags="${rflags:+${rflags},}logdev=$journaldev" + ;; + reiserfs) + fsckoptions="-j $journaldev $fsckoptions" + rflags="${rflags:+${rflags},}jdev=$journaldev" + ;; + *) ;; + esac + fi + + _rflags_ro="$rflags,ro" + _rflags_ro="${_rflags_ro##,}" + + while ! mount -t "${rootfs}" -o "$_rflags_ro" "${root#block:}" "$NEWROOT"; do + warn "Failed to mount -t ${rootfs} -o $_rflags_ro ${root#block:} $NEWROOT" + fsck_ask_err + done + + READONLY= + fsckoptions= + if [ -f "$NEWROOT"/etc/sysconfig/readonly-root ]; then + # shellcheck disable=SC1090 + . "$NEWROOT"/etc/sysconfig/readonly-root + fi + + if getargbool 0 "readonlyroot=" -y readonlyroot; then + READONLY=yes + fi + + if getarg noreadonlyroot; then + READONLY=no + fi + + if [ -f "$NEWROOT"/fastboot ] || getargbool 0 fastboot; then + fastboot=yes + fi + + if ! getargbool 0 rd.skipfsck; then + if [ -f "$NEWROOT"/fsckoptions ]; then + read -r fsckoptions < "$NEWROOT"/fsckoptions + fi + + if [ -f "$NEWROOT"/forcefsck ] || getargbool 0 forcefsck; then + fsckoptions="-f $fsckoptions" + elif [ -f "$NEWROOT"/.autofsck ]; then + # shellcheck disable=SC1090 + [ -f "$NEWROOT"/etc/sysconfig/autofsck ] \ + && . "$NEWROOT"/etc/sysconfig/autofsck + if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then + AUTOFSCK_OPT="$AUTOFSCK_OPT -f" + fi + if [ -n "$AUTOFSCK_SINGLEUSER" ]; then + warn "*** Warning -- the system did not shut down cleanly. " + warn "*** Dropping you to a shell; the system will continue" + warn "*** when you leave the shell." + emergency_shell + fi + fsckoptions="$AUTOFSCK_OPT $fsckoptions" + fi + fi + + rootopts= + if getargbool 1 rd.fstab -d -n rd_NO_FSTAB \ + && ! getarg rootflags > /dev/null \ + && [ -f "$NEWROOT/etc/fstab" ] \ + && ! [ -L "$NEWROOT/etc/fstab" ]; then + # if $NEWROOT/etc/fstab contains special mount options for + # the root filesystem, + # remount it with the proper options + rootopts="defaults" + while read -r dev mp fs opts _ fsck || [ -n "$dev" ]; do + # skip comments + [ "${dev%%#*}" != "$dev" ] && continue + + if [ "$mp" = "/" ]; then + # sanity - determine/fix fstype + rootfs=$(det_fs "${root#block:}" "$fs") + rootopts=$opts + rootfsck=$fsck + break + fi + done < "$NEWROOT/etc/fstab" + fi + + # we want rootflags (rflags) to take precedence so prepend rootopts to + # them + rflags="${rootopts},${rflags}" + rflags="${rflags#,}" + rflags="${rflags%,}" + + # backslashes are treated as escape character in fstab + # esc_root=$(echo ${root#block:} | sed 's,\\,\\\\,g') + # printf '%s %s %s %s 1 1 \n' "$esc_root" "$NEWROOT" "$rootfs" "$rflags" >/etc/fstab + + if fsck_able "$rootfs" \ + && [ "$rootfsck" != "0" -a -z "$fastboot" -a "$READONLY" != "yes" ] \ + && ! strstr "${rflags}" _netdev \ + && ! getargbool 0 rd.skipfsck; then + umount "$NEWROOT" + fsck_single "${root#block:}" "$rootfs" "$rflags" "$fsckoptions" + fi + + echo "${root#block:} $NEWROOT $rootfs ${rflags:-defaults} 0 ${rootfsck:-0}" >> /etc/fstab + + if ! ismounted "$NEWROOT"; then + info "Mounting ${root#block:} with -o ${rflags}" + mount "$NEWROOT" 2>&1 | vinfo + elif ! are_lists_eq , "$rflags" "$_rflags_ro" defaults; then + info "Remounting ${root#block:} with -o ${rflags}" + mount -o remount "$NEWROOT" 2>&1 | vinfo + fi + + if ! getargbool 0 rd.skipfsck; then + [ -f "$NEWROOT"/forcefsck ] && rm -f -- "$NEWROOT"/forcefsck 2> /dev/null + [ -f "$NEWROOT"/.autofsck ] && rm -f -- "$NEWROOT"/.autofsck 2> /dev/null + fi +} + +if [ -n "$root" -a -z "${root%%block:*}" ]; then + mount_root +fi diff --git a/modules.d/95rootfs-block/parse-block.sh b/modules.d/95rootfs-block/parse-block.sh new file mode 100755 index 0000000..9c4357e --- /dev/null +++ b/modules.d/95rootfs-block/parse-block.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +case "${root#block:}" in + LABEL=* | UUID=* | PARTUUID=* | PARTLABEL=*) + root="block:$(label_uuid_to_dev "$root")" + rootok=1 + ;; + /dev/*) + root="block:${root#block:}" + # shellcheck disable=SC2034 + rootok=1 + ;; +esac + +[ "${root%%:*}" = "block" ] && wait_for_dev "${root#block:}" diff --git a/modules.d/95rootfs-block/rootfallback.sh b/modules.d/95rootfs-block/rootfallback.sh new file mode 100755 index 0000000..ce4ee85 --- /dev/null +++ b/modules.d/95rootfs-block/rootfallback.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +for root in $(getargs rootfallback=); do + root=$(label_uuid_to_dev "$root") + + if ! [ -b "$root" ]; then + warn "Could not find rootfallback $root" + continue + fi + + if mount "$root" /sysroot; then + info "Mounted rootfallback $root" + exit 0 + else + warn "Failed to mount rootfallback $root" + exit 1 + fi +done + +[ -e "$job" ] && rm -f "$job" diff --git a/modules.d/95ssh-client/module-setup.sh b/modules.d/95ssh-client/module-setup.sh new file mode 100755 index 0000000..75fc94f --- /dev/null +++ b/modules.d/95ssh-client/module-setup.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# fixme: assume user is root + +# called by dracut +check() { + [[ $mount_needs ]] && return 1 + + # If our prerequisites are not met, fail. + require_binaries ssh scp || return 1 + + if [[ $sshkey ]]; then + [[ ! -f $dracutsysrootdir$sshkey ]] && { + derror "ssh key: $sshkey is not found!" + return 1 + } + fi + + return 255 +} + +# called by dracut +depends() { + # We depend on network modules being loaded + echo network +} + +inst_sshenv() { + if [[ -d $dracutsysrootdir/root/.ssh ]]; then + inst_dir /root/.ssh + chmod 700 "${initdir}"/root/.ssh + fi + + # Copy over ssh key and knowhosts if needed + [[ $sshkey ]] && { + inst_simple "$sshkey" + [[ -f $dracutsysrootdir/root/.ssh/known_hosts ]] && inst_simple /root/.ssh/known_hosts + [[ -f $dracutsysrootdir/etc/ssh/ssh_known_hosts ]] && inst_simple /etc/ssh/ssh_known_hosts + } + + # Copy over root and system-wide ssh configs. + [[ -f $dracutsysrootdir/root/.ssh/config ]] && inst_simple /root/.ssh/config + if [[ -f $dracutsysrootdir/etc/ssh/ssh_config ]]; then + inst_simple /etc/ssh/ssh_config + sed -i -e 's/\(^[[:space:]]*\)ProxyCommand/\1# ProxyCommand/' "${initdir}"/etc/ssh/ssh_config + while read -r key val || [ -n "$key" ]; do + if [[ $key == "GlobalKnownHostsFile" ]]; then + inst_simple "$val" + # Copy customized UserKnowHostsFile + elif [[ $key == "UserKnownHostsFile" ]]; then + # Make sure that ~/foo will be copied as /root/foo in kdump's initramfs + # shellcheck disable=SC2088 + if str_starts "$val" "~/"; then + val="/root/${val#"~/"}" + fi + inst_simple "$val" + fi + done < "$dracutsysrootdir"/etc/ssh/ssh_config + fi + + return 0 +} + +# called by dracut +install() { + local _nsslibs + + inst_multiple ssh scp + inst_sshenv + + _nsslibs=$( + cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \ + | sed -e 's/#.*//; s/^[^:]*://; s/\[[^]]*\]//' \ + | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|' + ) + _nsslibs=${_nsslibs#|} + _nsslibs=${_nsslibs%|} + + inst_libdir_file -n "$_nsslibs" 'libnss_*.so*' +} diff --git a/modules.d/95terminfo/module-setup.sh b/modules.d/95terminfo/module-setup.sh new file mode 100755 index 0000000..8cecaf3 --- /dev/null +++ b/modules.d/95terminfo/module-setup.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# called by dracut +install() { + local _terminfodir + # terminfo bits make things work better if you fall into interactive mode + for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do + [[ -f $dracutsysrootdir${_terminfodir}/l/linux ]] && break + done + + if [[ -d $dracutsysrootdir${_terminfodir} ]]; then + for i in "l/linux" "v/vt100" "v/vt102" "v/vt220"; do + inst_dir "$_terminfodir/${i%/*}" + $DRACUT_CP -L -t "${initdir}/${_terminfodir}/${i%/*}" "$dracutsysrootdir$_terminfodir/$i" + done + fi +} diff --git a/modules.d/95udev-rules/59-persistent-storage.rules b/modules.d/95udev-rules/59-persistent-storage.rules new file mode 100644 index 0000000..b076937 --- /dev/null +++ b/modules.d/95udev-rules/59-persistent-storage.rules @@ -0,0 +1,9 @@ +SUBSYSTEM!="block", GOTO="ps_end" +ACTION!="add|change", GOTO="ps_end" +# Also don't process disks that are slated to be a multipath device +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="ps_end" + +KERNEL=="cciss[0-9]*", IMPORT{builtin}="blkid" +KERNEL=="nbd[0-9]*", IMPORT{builtin}="blkid" + +LABEL="ps_end" diff --git a/modules.d/95udev-rules/61-persistent-storage.rules b/modules.d/95udev-rules/61-persistent-storage.rules new file mode 100644 index 0000000..053b65c --- /dev/null +++ b/modules.d/95udev-rules/61-persistent-storage.rules @@ -0,0 +1,22 @@ +SUBSYSTEM!="block", GOTO="pss_end" +ACTION!="add|change", GOTO="pss_end" +# Also don't process disks that are slated to be a multipath device +ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="pss_end" + +ACTION=="change", KERNEL=="dm-[0-9]*", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}!="1", GOTO="do_pss" +KERNEL=="cciss[0-9]*", GOTO="do_pss" +KERNEL=="nbd[0-9]*", GOTO="do_pss" +KERNEL=="md[0-9]*|md_d[0-9]*|md/*", GOTO="do_pss" + +GOTO="pss_end" + +LABEL="do_pss" +# by-path (parent device path) +ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id" +ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" +ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" + +# by-label/by-uuid links (filesystem metadata) +ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" +ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" +LABEL="pss_end" diff --git a/modules.d/95udev-rules/module-setup.sh b/modules.d/95udev-rules/module-setup.sh new file mode 100755 index 0000000..3ca12ee --- /dev/null +++ b/modules.d/95udev-rules/module-setup.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# called by dracut +install() { + local _i + + # Fixme: would be nice if we didn't have to guess, which rules to grab.... + # ultimately, /lib/initramfs/rules.d or somesuch which includes links/copies + # of the rules we want so that we just copy those in would be best + inst_multiple udevadm cat uname blkid + inst_dir /etc/udev + inst_multiple -o /etc/udev/udev.conf + + [[ -d ${initdir}/$systemdutildir ]] || mkdir -p "${initdir}/$systemdutildir" + for _i in "${systemdutildir}"/systemd-udevd "${udevdir}"/udevd /sbin/udevd; do + [[ -x $dracutsysrootdir$_i ]] || continue + inst "$_i" + + if ! [[ -f ${initdir}${systemdutildir}/systemd-udevd ]]; then + ln -fs "$_i" "${initdir}${systemdutildir}"/systemd-udevd + fi + break + done + if ! [[ -e ${initdir}${systemdutildir}/systemd-udevd ]]; then + derror "Cannot find [systemd-]udevd binary!" + exit 1 + fi + + inst_rules \ + 50-udev-default.rules \ + 55-scsi-sg3_id.rules \ + 58-scsi-sg3_symlink.rules \ + 59-scsi-sg3_utils.rules \ + 60-block.rules \ + 60-cdrom_id.rules \ + 60-pcmcia.rules \ + 60-persistent-storage.rules \ + 64-btrfs.rules \ + 70-uaccess.rules \ + 71-seat.rules \ + 73-seat-late.rules \ + 75-net-description.rules \ + 80-drivers.rules 95-udev-late.rules \ + 80-net-name-slot.rules 80-net-setup-link.rules \ + "$moddir/59-persistent-storage.rules" \ + "$moddir/61-persistent-storage.rules" + + # legacy persistent network device name rules + [[ $hostonly ]] && inst_rules 70-persistent-net.rules + + { + for i in cdrom tape dialout floppy; do + if ! grep -q "^$i:" "$initdir"/etc/group 2> /dev/null; then + if ! grep "^$i:" "$dracutsysrootdir"/etc/group 2> /dev/null; then + case $i in + cdrom) echo "$i:x:11:" ;; + dialout) echo "$i:x:18:" ;; + floppy) echo "$i:x:19:" ;; + tape) echo "$i:x:33:" ;; + esac + fi + fi + done + } >> "$initdir/etc/group" + + inst_multiple -o \ + "${udevdir}"/ata_id \ + "${udevdir}"/cdrom_id \ + "${udevdir}"/create_floppy_devices \ + "${udevdir}"/fw_unit_symlinks.sh \ + "${udevdir}"/hid2hci \ + "${udevdir}"/path_id \ + "${udevdir}"/input_id \ + "${udevdir}"/scsi_id \ + "${udevdir}"/usb_id \ + "${udevdir}"/pcmcia-socket-startup \ + "${udevdir}"/pcmcia-check-broken-cis + + inst_multiple -o /etc/pcmcia/config.opts + + inst_libdir_file "libnss_files*" + +} diff --git a/modules.d/95virtfs/module-setup.sh b/modules.d/95virtfs/module-setup.sh new file mode 100755 index 0000000..8026002 --- /dev/null +++ b/modules.d/95virtfs/module-setup.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# called by dracut +check() { + [[ $hostonly ]] || [[ $mount_needs ]] && { + for fs in "${host_fs_types[@]}"; do + [[ $fs == "9p" ]] && return 0 + done + return 255 + } + + is_qemu_virtualized && return 0 + + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + instmods 9p 9pnet_virtio virtio_pci +} + +# called by dracut +install() { + inst_hook cmdline 95 "$moddir/parse-virtfs.sh" + inst_hook mount 99 "$moddir/mount-virtfs.sh" +} diff --git a/modules.d/95virtfs/mount-virtfs.sh b/modules.d/95virtfs/mount-virtfs.sh new file mode 100755 index 0000000..e52c752 --- /dev/null +++ b/modules.d/95virtfs/mount-virtfs.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +filter_rootopts() { + rootopts=$1 + # strip ro and rw options + local OLDIFS="$IFS" + IFS=, + # shellcheck disable=SC2086 + set -- $rootopts + IFS="$OLDIFS" + local v + while [ $# -gt 0 ]; do + case $1 in + rw | ro) ;; + defaults) ;; + *) + v="$v,${1}" + ;; + esac + shift + done + rootopts=${v#,} + echo "$rootopts" +} + +mount_root() { + rootfs="9p" + rflags="trans=virtio,version=9p2000.L" + + modprobe 9pnet_virtio + + mount -t ${rootfs} -o "$rflags",ro "${root#virtfs:}" "$NEWROOT" + + rootopts= + if getargbool 1 rd.fstab -n rd_NO_FSTAB \ + && ! getarg rootflags \ + && [ -f "$NEWROOT/etc/fstab" ] \ + && ! [ -L "$NEWROOT/etc/fstab" ]; then + # if $NEWROOT/etc/fstab contains special mount options for + # the root filesystem, + # remount it with the proper options + rootopts="defaults" + while read -r dev mp _ opts rest || [ -n "$dev" ]; do + # skip comments + [ "${dev%%#*}" != "$dev" ] && continue + + if [ "$mp" = "/" ]; then + rootopts=$opts + break + fi + done < "$NEWROOT/etc/fstab" + + rootopts=$(filter_rootopts "$rootopts") + fi + + # we want rootflags (rflags) to take precedence so prepend rootopts to + # them; rflags is guaranteed to not be empty + rflags="${rootopts:+${rootopts},}${rflags}" + + umount "$NEWROOT" + + info "Remounting ${root#virtfs:} with -o ${rflags}" + mount -t ${rootfs} -o "$rflags" "${root#virtfs:}" "$NEWROOT" 2>&1 | vinfo + + [ -f "$NEWROOT"/forcefsck ] && rm -f -- "$NEWROOT"/forcefsck 2> /dev/null + [ -f "$NEWROOT"/.autofsck ] && rm -f -- "$NEWROOT"/.autofsck 2> /dev/null +} + +if [ -n "$root" -a -z "${root%%virtfs:*}" ]; then + mount_root +fi +: diff --git a/modules.d/95virtfs/parse-virtfs.sh b/modules.d/95virtfs/parse-virtfs.sh new file mode 100755 index 0000000..9f67123 --- /dev/null +++ b/modules.d/95virtfs/parse-virtfs.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +if [ "${root%%:*}" = "virtfs" ]; then + modprobe 9pnet_virtio + + # shellcheck disable=SC2034 + rootok=1 +fi diff --git a/modules.d/95virtiofs/module-setup.sh b/modules.d/95virtiofs/module-setup.sh new file mode 100755 index 0000000..176482b --- /dev/null +++ b/modules.d/95virtiofs/module-setup.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# called by dracut +check() { + [[ $hostonly ]] || [[ $mount_needs ]] && { + is_qemu_virtualized && return 0 + + for fs in "${host_fs_types[@]}"; do + [[ $fs == "virtiofs" ]] && return 0 + done + return 255 + } + + return 0 +} + +# called by dracut +depends() { + echo base +} + +# called by dracut +installkernel() { + instmods virtiofs virtio_pci +} + +# called by dracut +install() { + inst_hook cmdline 95 "$moddir/parse-virtiofs.sh" + inst_hook pre-mount 99 "$moddir/mount-virtiofs.sh" +} diff --git a/modules.d/95virtiofs/mount-virtiofs.sh b/modules.d/95virtiofs/mount-virtiofs.sh new file mode 100755 index 0000000..3d73884 --- /dev/null +++ b/modules.d/95virtiofs/mount-virtiofs.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +type ismounted > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if [ "${fstype}" = "virtiofs" -o "${root%%:*}" = "virtiofs" ]; then + if ! load_fstype virtiofs; then + die "virtiofs is required but not available." + fi + + mount -t virtiofs -o "$rflags" "${root#virtiofs:}" "$NEWROOT" 2>&1 | vinfo + if ! ismounted "$NEWROOT"; then + die "virtiofs: failed to mount root fs" + fi + + info "virtiofs: root fs mounted (options: '${rflags}')" + + [ -f "$NEWROOT"/forcefsck ] && rm -f -- "$NEWROOT"/forcefsck 2> /dev/null + [ -f "$NEWROOT"/.autofsck ] && rm -f -- "$NEWROOT"/.autofsck 2> /dev/null +fi +: diff --git a/modules.d/95virtiofs/parse-virtiofs.sh b/modules.d/95virtiofs/parse-virtiofs.sh new file mode 100755 index 0000000..760e413 --- /dev/null +++ b/modules.d/95virtiofs/parse-virtiofs.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Accepted formats: +# rootfstype=virtiofs root=<tag> +# root=virtiofs:<tag> + +if [ "${fstype}" = "virtiofs" -o "${root%%:*}" = "virtiofs" ]; then + # shellcheck disable=SC2034 + rootok=1 +fi diff --git a/modules.d/95zfcp/module-setup.sh b/modules.d/95zfcp/module-setup.sh new file mode 100755 index 0000000..e1f3aa3 --- /dev/null +++ b/modules.d/95zfcp/module-setup.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# called by dracut +check() { + arch=${DRACUT_ARCH:-$(uname -m)} + [ "$arch" = "s390" -o "$arch" = "s390x" ] || return 1 + + require_binaries zfcp_cio_free grep sed seq || return 1 + + return 0 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + instmods zfcp +} + +# called by dracut +install() { + inst_hook cmdline 30 "$moddir/parse-zfcp.sh" + inst_multiple zfcp_cio_free grep sed seq + + inst_script /sbin/zfcpconf.sh + inst_rules 56-zfcp.rules + + if [[ $hostonly ]]; then + inst_simple -H /etc/zfcp.conf + fi +} diff --git a/modules.d/95zfcp/parse-zfcp.sh b/modules.d/95zfcp/parse-zfcp.sh new file mode 100755 index 0000000..495aa67 --- /dev/null +++ b/modules.d/95zfcp/parse-zfcp.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +getargbool 1 rd.zfcp.conf -d -n rd_NO_ZFCPCONF || rm /etc/zfcp.conf + +for zfcp_arg in $(getargs rd.zfcp -d 'rd_ZFCP='); do + echo "$zfcp_arg" | grep '^0\.[0-9a-fA-F]\.[0-9a-fA-F]\{4\}\(,0x[0-9a-fA-F]\{16\},0x[0-9a-fA-F]\{16\}\)\?$' > /dev/null + test $? -ne 0 && die "For argument 'rd.zfcp=$zfcp_arg'\nSorry, invalid format." + ( + IFS="," + # shellcheck disable=SC2086 + set $zfcp_arg + echo "$@" >> /etc/zfcp.conf + ) +done + +zfcp_cio_free diff --git a/modules.d/95zfcp_rules/module-setup.sh b/modules.d/95zfcp_rules/module-setup.sh new file mode 100755 index 0000000..dfa7951 --- /dev/null +++ b/modules.d/95zfcp_rules/module-setup.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# called by dracut +cmdline() { + is_zfcp() { + local _dev=$1 + local _devpath + _devpath=$( + cd -P /sys/dev/block/"$_dev" || exit + echo "$PWD" + ) + local _sdev _scsiid _hostno _lun _wwpn _ccw _port_type + local _allow_lun_scan _is_npiv + + read -r _allow_lun_scan < /sys/module/zfcp/parameters/allow_lun_scan + [ "${_devpath#*/sd}" == "$_devpath" ] && return 1 + _sdev="${_devpath%%/block/*}" + [ -e "${_sdev}"/fcp_lun ] || return 1 + _scsiid="${_sdev##*/}" + _hostno="${_scsiid%%:*}" + [ -d /sys/class/fc_host/host"${_hostno}" ] || return 1 + read -r _port_type < /sys/class/fc_host/host"${_hostno}"/port_type + case "$_port_type" in + NPIV*) + _is_npiv=1 + ;; + esac + read -r _ccw < "${_sdev}"/hba_id + if [ "$_is_npiv" ] && [ "$_allow_lun_scan" = "Y" ]; then + echo "rd.zfcp=${_ccw}" + else + read -r _lun < "${_sdev}"/fcp_lun + read -r _wwpn < "${_sdev}"/wwpn + echo "rd.zfcp=${_ccw},${_wwpn},${_lun}" + fi + return 0 + } + [[ $hostonly ]] || [[ $mount_needs ]] && { + for_each_host_dev_and_slaves_all is_zfcp + } | sort | uniq +} + +# called by dracut +check() { + local _arch=${DRACUT_ARCH:-$(uname -m)} + local _ccw + [ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + found=0 + for _ccw in /sys/bus/ccw/devices/*/host*; do + [ -d "$_ccw" ] || continue + found=$((found + 1)) + done + [ $found -eq 0 ] && return 255 + } + return 0 +} + +# called by dracut +depends() { + echo bash + return 0 +} + +# called by dracut +install() { + inst_hook cmdline 30 "$moddir/parse-zfcp.sh" + if [[ $hostonly_cmdline == "yes" ]]; then + local _zfcp + + for _zfcp in $(cmdline); do + printf "%s\n" "$_zfcp" >> "${initdir}/etc/cmdline.d/94zfcp.conf" + done + fi + if [[ $hostonly ]]; then + inst_rules_wildcard "51-zfcp-*.rules" + inst_rules_wildcard "41-zfcp-*.rules" + fi +} diff --git a/modules.d/95zfcp_rules/parse-zfcp.sh b/modules.d/95zfcp_rules/parse-zfcp.sh new file mode 100755 index 0000000..5e7d909 --- /dev/null +++ b/modules.d/95zfcp_rules/parse-zfcp.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +create_udev_rule() { + local ccw=$1 + local wwpn=$2 + local lun=$3 + local _rule=/etc/udev/rules.d/51-zfcp-${ccw}.rules + local _cu_type _dev_type + + if [ -x /sbin/cio_ignore ] && cio_ignore -i "$ccw" > /dev/null; then + cio_ignore -r "$ccw" + fi + + if [ -e /sys/bus/ccw/devices/"${ccw}" ]; then + read -r _cu_type < /sys/bus/ccw/devices/"${ccw}"/cutype + read -r _dev_type < /sys/bus/ccw/devices/"${ccw}"/devtype + fi + if [ "$_cu_type" != "1731/03" ]; then + return 0 + fi + if [ "$_dev_type" != "1732/03" ] && [ "$_dev_type" != "1732/04" ]; then + return 0 + fi + + [ -z "$wwpn" ] || [ -z "$lun" ] && return + m=$(sed -n "/.*${wwpn}.*${lun}.*/p" "$_rule") + if [ -z "$m" ]; then + cat >> "$_rule" << EOF +ACTION=="add", KERNEL=="rport-*", ATTR{port_name}=="$wwpn", SUBSYSTEMS=="ccw", KERNELS=="$ccw", ATTR{[ccw/$ccw]$wwpn/unit_add}="$lun" +EOF + fi +} + +if [[ -f /sys/firmware/ipl/ipl_type ]] \ + && [[ $(< /sys/firmware/ipl/ipl_type) == "fcp" ]]; then + ( + read -r _wwpn < /sys/firmware/ipl/wwpn + read -r _lun < /sys/firmware/ipl/lun + read -r _ccw < /sys/firmware/ipl/device + + create_udev_rule "$_ccw" "$_wwpn" "$_lun" + ) +fi + +for zfcp_arg in $(getargs rd.zfcp); do + ( + OLDIFS="$IFS" + IFS="," + # shellcheck disable=SC2086 + set $zfcp_arg + IFS="$OLDIFS" + create_udev_rule "$1" "$2" "$3" + ) +done + +for zfcp_arg in $(getargs root=) $(getargs resume=); do + ( + case $zfcp_arg in + /dev/disk/by-path/ccw-*) + ccw_arg=${zfcp_arg##*/} + ;; + esac + if [ -n "$ccw_arg" ]; then + OLDIFS="$IFS" + IFS="-" + set -- "$ccw_arg" + IFS="$OLDIFS" + _wwpn=${4%:*} + _lun=${4#*:} + create_udev_rule "$2" "$wwpn" "$lun" + fi + ) +done diff --git a/modules.d/95znet/module-setup.sh b/modules.d/95znet/module-setup.sh new file mode 100755 index 0000000..df37c66 --- /dev/null +++ b/modules.d/95znet/module-setup.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# called by dracut +check() { + arch=${DRACUT_ARCH:-$(uname -m)} + [ "$arch" = "s390" -o "$arch" = "s390x" ] || return 1 + + require_binaries znet_cio_free grep sed seq readlink || return 1 + + return 0 +} + +# called by dracut +depends() { + echo bash + return 0 +} + +# called by dracut +installkernel() { + instmods ctcm lcs qeth qeth_l2 qeth_l3 +} + +# called by dracut +install() { + inst_hook cmdline 30 "$moddir/parse-ccw.sh" + inst_rules 81-ccw.rules + inst_multiple znet_cio_free grep sed seq readlink /lib/udev/ccw_init +} diff --git a/modules.d/95znet/parse-ccw.sh b/modules.d/95znet/parse-ccw.sh new file mode 100755 index 0000000..d895360 --- /dev/null +++ b/modules.d/95znet/parse-ccw.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +for ccw_arg in $(getargs rd.ccw -d 'rd_CCW=') $(getargs rd.znet -d 'rd_ZNET='); do + echo "$ccw_arg" >> /etc/ccw.conf +done + +for ifname in $(getargs rd.znet_ifname); do + IFS=: read -r ifname_if ifname_subchannels _rest <<< "$ifname" + if [ -z "$ifname_if" ] || [ -z "$ifname_subchannels" ] || [ -n "$_rest" ]; then + warn "Invalid arguments for rd.znet_ifname=" + else + { + ifname_subchannels=${ifname_subchannels//,/|} + + echo 'ACTION!="add|change", GOTO="ccw_ifname_end"' + echo 'ATTR{type}!="1", GOTO="ccw_ifname_end"' + echo 'SUBSYSTEM!="net", GOTO="ccw_ifname_end"' + echo "SUBSYSTEMS==\"ccwgroup\", KERNELS==\"$ifname_subchannels\", DRIVERS==\"?*\" NAME=\"$ifname_if\"" + echo 'LABEL="ccw_ifname_end"' + + } > /etc/udev/rules.d/81-ccw-ifname.rules + fi +done + +znet_cio_free diff --git a/modules.d/96securityfs/module-setup.sh b/modules.d/96securityfs/module-setup.sh new file mode 100755 index 0000000..1181b71 --- /dev/null +++ b/modules.d/96securityfs/module-setup.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_hook cmdline 60 "$moddir/securityfs.sh" +} diff --git a/modules.d/96securityfs/securityfs.sh b/modules.d/96securityfs/securityfs.sh new file mode 100755 index 0000000..2493c1d --- /dev/null +++ b/modules.d/96securityfs/securityfs.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +SECURITYFSDIR="/sys/kernel/security" +export SECURITYFSDIR + +if ! findmnt "${SECURITYFSDIR}" > /dev/null 2>&1; then + mount -t securityfs -o nosuid,noexec,nodev securityfs ${SECURITYFSDIR} > /dev/null 2>&1 +fi diff --git a/modules.d/97biosdevname/module-setup.sh b/modules.d/97biosdevname/module-setup.sh new file mode 100755 index 0000000..c10200d --- /dev/null +++ b/modules.d/97biosdevname/module-setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# called by dracut +check() { + [[ "$mount_needs" ]] && return 1 + require_binaries biosdevname || return 1 + return 0 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_multiple biosdevname + inst_rules 71-biosdevname.rules +} diff --git a/modules.d/97biosdevname/parse-biosdevname.sh b/modules.d/97biosdevname/parse-biosdevname.sh new file mode 100755 index 0000000..452873f --- /dev/null +++ b/modules.d/97biosdevname/parse-biosdevname.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +if ! getargbool 1 biosdevname; then + info "biosdevname=0: removing biosdevname network renaming" + udevproperty UDEV_BIOSDEVNAME= + rm -f -- /etc/udev/rules.d/71-biosdevname.rules +else + info "biosdevname=1: activating biosdevname network renaming" + udevproperty UDEV_BIOSDEVNAME=1 +fi diff --git a/modules.d/97masterkey/README b/modules.d/97masterkey/README new file mode 100644 index 0000000..524ccfc --- /dev/null +++ b/modules.d/97masterkey/README @@ -0,0 +1,68 @@ +# Directions for creating the kernel master key that will be used for +# encrypting/decrypting other keys. + +# A trusted key is a TPM random number, which is only ever exposed to +# userspace as an encrypted datablob. A trusted key can be sealed to a +# set of PCR values. For more details on trusted keys, refer to the +# kernel keys-trusted-encrypted.txt documentation. +$ keyctl add trusted kmk-trusted "new 32" @u +801713097 + +# For those systems which don't have a TPM, but want to experiment with +# encrypted keys, create a user key of 32 random bytes. Unlike +# trusted/encrypted keys, user type key data is visible to userspace. +$ keyctl add user kmk-user "`dd if=/dev/urandom bs=1 count=32 2>/dev/null`" @u +144468621 + +# Save the kernel master key (trusted type): +$ su -c 'keyctl pipe `keyctl search @u trusted kmk-trusted` > /etc/keys/kmk-trusted.blob' + +# or (user type): +$ su -c 'keyctl pipe `keyctl search @u user kmk-user` > /etc/keys/kmk-user.blob' + +# A useful feature of trusted keys is that it is possible to prevent their +# unsealing at later time by providing the parameter 'pcrlock=<pcrnum>' when +# loading it, which causes the PCR #<pcrnum> to be extended with a random value. +# Actually, the <pcrnum> variable is set to '11' to let users experiment with +# this feature by using a register that is never extended during the boot, +# making the re-sealing not necessary. In the future, the kernel master key will +# be sealed to the PCR #14 which is extended, according to the TrustedGRUB +# documentation[1], to the measure of the kernel and the initial ramdisk. + +# The kernel master key path name and type can be set in one of the following +# ways (specified in the order in which variables are overwritten): + +1) use default values: +-------------------------------------------------------------------------- +MULTIKERNELMODE="NO" +MASTERKEYTYPE="trusted" +MASTERKEY="/etc/keys/kmk-${MASTERKEYTYPE}.blob" +-------------------------------------------------------------------------- + +2) create the configuration file '/etc/sysconfig/masterkey' to override the +value of one or all variables; + +3) specify these parameters in the kernel command line: +- masterkey=</kernel/master/key/path>, to override the MASTERKEY variable; +- masterkeytype=<kernel-master-key-type>, to override the MASTERKEYTYPE variable. + +# The variable MULTIKERNELMODE has been introduced to support multi boot +# configurations, where a trusted/user key is tied to a specific kernel and +# initial ramdisk. In this case, setting MULTIKERNELMODE to 'YES' will cause the +# kernel version to be added to the default masterkey path name, so that the +# MASTERKEY variable should not be overridden each time a different kernel is +# chosen. The default value of MASTERKEY will be equal to: +-------------------------------------------------------------------------- +MASTERKEY="/etc/keys/kmk-${MASTERKEYTYPE}-$(uname -r).blob" +-------------------------------------------------------------------------- + +# The masterkey path name also depends on the value of MASTERKEYTYPE, as reported +# in the default values for defined variables. For example, if only MASTERKEYTYPE +# is overridden by setting it to 'user' in the configuration file or from the +# kernel command line, the value of MASTERKEY will be: +-------------------------------------------------------------------------- +MASTERKEY="/etc/keys/kmk-user.blob" +-------------------------------------------------------------------------- + + +[1] https://projects.sirrix.com/trac/trustedgrub/ diff --git a/modules.d/97masterkey/masterkey.sh b/modules.d/97masterkey/masterkey.sh new file mode 100755 index 0000000..6dd6a4d --- /dev/null +++ b/modules.d/97masterkey/masterkey.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +# Licensed under the GPLv2 +# +# Copyright (C) 2011 Politecnico di Torino, Italy +# TORSEC group -- http://security.polito.it +# Roberto Sassu <roberto.sassu@polito.it> + +MASTERKEYSCONFIG="${NEWROOT}/etc/sysconfig/masterkey" +MULTIKERNELMODE="NO" +PCRLOCKNUM=11 + +load_masterkey() { + # read the configuration from the config file + # shellcheck disable=SC1090 + [ -f "${MASTERKEYSCONFIG}" ] \ + && . "${MASTERKEYSCONFIG}" + + # override the kernel master key path name from the 'masterkey=' parameter + # in the kernel command line + MASTERKEYARG=$(getarg masterkey=) && MASTERKEY=${MASTERKEYARG} + + # override the kernel master key type from the 'masterkeytype=' parameter + # in the kernel command line + MASTERKEYTYPEARG=$(getarg masterkeytype=) && MASTERKEYTYPE=${MASTERKEYTYPEARG} + + # set default values + [ -z "${MASTERKEYTYPE}" ] \ + && MASTERKEYTYPE="trusted" + + if [ -z "${MASTERKEY}" ]; then + # append the kernel version to the default masterkey path name + # if MULTIKERNELMODE is set to YES + if [ "${MULTIKERNELMODE}" = "YES" ]; then + MASTERKEY="/etc/keys/kmk-${MASTERKEYTYPE}-$(uname -r).blob" + else + MASTERKEY="/etc/keys/kmk-${MASTERKEYTYPE}.blob" + fi + fi + + # set the kernel master key path name + MASTERKEYPATH="${NEWROOT}${MASTERKEY}" + + # check for kernel master key's existence + if [ ! -f "${MASTERKEYPATH}" ]; then + if [ "${RD_DEBUG}" = "yes" ]; then + info "masterkey: kernel master key file not found: ${MASTERKEYPATH}" + fi + return 1 + fi + + # read the kernel master key blob + read -r KEYBLOB < "${MASTERKEYPATH}" + + # add the 'load' prefix if the key type is 'trusted' + [ "${MASTERKEYTYPE}" = "trusted" ] \ + && KEYBLOB="load ${KEYBLOB} pcrlock=${PCRLOCKNUM}" + + # load the kernel master key + info "Loading the kernel master key" + keyctl add "${MASTERKEYTYPE}" "kmk-${MASTERKEYTYPE}" "${KEYBLOB}" @u > /dev/null || { + info "masterkey: failed to load the kernel master key: kmk-${MASTERKEYTYPE}" + return 1 + } + + return 0 +} + +load_masterkey diff --git a/modules.d/97masterkey/module-setup.sh b/modules.d/97masterkey/module-setup.sh new file mode 100755 index 0000000..49b006e --- /dev/null +++ b/modules.d/97masterkey/module-setup.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# called by dracut +check() { + [[ $hostonly ]] && { + require_binaries keyctl uname || return 1 + } + + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +installkernel() { + instmods trusted encrypted +} + +# called by dracut +install() { + inst_multiple keyctl uname + inst_hook pre-pivot 60 "$moddir/masterkey.sh" +} diff --git a/modules.d/98dracut-systemd/dracut-cmdline-ask.service b/modules.d/98dracut-systemd/dracut-cmdline-ask.service new file mode 100644 index 0000000..c9458b7 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-cmdline-ask.service @@ -0,0 +1,32 @@ +# This file is part of dracut. + +[Unit] +Description=dracut ask for additional cmdline parameters +Documentation=man:dracut.bootup(7) +DefaultDependencies=no +Before=dracut-cmdline.service +Wants=systemd-journald.socket +After=systemd-journald.socket +Wants=systemd-vconsole-setup.service +After=systemd-vconsole-setup.service + +ConditionPathExists=/usr/lib/initrd-release +ConditionKernelCommandLine=|rd.cmdline=ask +ConditionPathExistsGlob=|/etc/cmdline.d/*.conf +Conflicts=shutdown.target emergency.target + +[Service] +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +Type=oneshot +ExecStart=-/bin/dracut-cmdline-ask +StandardInput=tty +StandardOutput=inherit +StandardError=inherit +RemainAfterExit=yes +KillMode=process +IgnoreSIGPIPE=no + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-cmdline-ask.sh b/modules.d/98dracut-systemd/dracut-cmdline-ask.sh new file mode 100755 index 0000000..13c4f20 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-cmdline-ask.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +getarg "rd.cmdline=ask" || exit 0 + +sleep 0.5 +echo +sleep 0.5 +echo +sleep 0.5 +echo +echo +echo +echo +echo "Enter additional kernel command line parameter (end with ctrl-d or .)" +while read -r -p "> " ${BASH:+-e} line || [ -n "$line" ]; do + [ "$line" = "." ] && break + [ -n "$line" ] && printf -- "%s\n" "$line" >> /etc/cmdline.d/99-cmdline-ask.conf +done + +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-cmdline.service b/modules.d/98dracut-systemd/dracut-cmdline.service new file mode 100644 index 0000000..ed71d58 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-cmdline.service @@ -0,0 +1,30 @@ +# This file is part of dracut. + +[Unit] +Description=dracut cmdline hook +Documentation=man:dracut-cmdline.service(8) man:dracut.bootup(7) +DefaultDependencies=no +Before=dracut-pre-udev.service +After=systemd-journald.socket +Wants=systemd-journald.socket +ConditionPathExists=/usr/lib/initrd-release +ConditionPathExistsGlob=|/etc/cmdline.d/*.conf +ConditionDirectoryNotEmpty=|/lib/dracut/hooks/cmdline +ConditionKernelCommandLine=|rd.break=cmdline +ConditionKernelCommandLine=|resume +ConditionKernelCommandLine=|noresume +Conflicts=shutdown.target emergency.target + +[Service] +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +Type=oneshot +ExecStart=-/bin/dracut-cmdline +StandardInput=null +StandardError=journal+console +KillMode=process +RemainAfterExit=yes + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-cmdline.service.8.asc b/modules.d/98dracut-systemd/dracut-cmdline.service.8.asc new file mode 100644 index 0000000..859448e --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-cmdline.service.8.asc @@ -0,0 +1,26 @@ +DRACUT-CMDLINE.SERVICE(8) +========================= +:doctype: manpage +:man source: dracut +:man manual: dracut + +NAME +---- +dracut-cmdline.service - runs the dracut hooks to parse the kernel command line + +SYNOPSIS +-------- +dracut-cmdline.service + +DESCRIPTION +----------- +This service runs all the dracut hooks to parse the kernel command line in +the initramfs. + +AUTHORS +------- +Harald Hoyer + +SEE ALSO +-------- +*dracut.bootup*(7) *dracut*(8) diff --git a/modules.d/98dracut-systemd/dracut-cmdline.sh b/modules.d/98dracut-systemd/dracut-cmdline.sh new file mode 100755 index 0000000..646fead --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-cmdline.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +if [ -f /dracut-state.sh ]; then + . /dracut-state.sh 2> /dev/null +fi +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +[ -f /usr/lib/initrd-release ] && . /usr/lib/initrd-release +[ -n "$VERSION" ] && info "dracut-$VERSION" + +if ! getargbool 1 'rd.hostonly'; then + [ -f /etc/cmdline.d/99-cmdline-ask.conf ] && mv /etc/cmdline.d/99-cmdline-ask.conf /tmp/99-cmdline-ask.conf + remove_hostonly_files + systemctl --no-block daemon-reload + [ -f /tmp/99-cmdline-ask.conf ] && mv /tmp/99-cmdline-ask.conf /etc/cmdline.d/99-cmdline-ask.conf +fi + +info "Using kernel command line parameters:" "$(getcmdline)" + +getargbool 0 rd.udev.log-priority=info -d rd.udev.info -d -n -y rdudevinfo && echo 'udev_log="info"' >> /etc/udev/udev.conf +getargbool 0 rd.udev.log-priority=debug -d rd.udev.debug -d -n -y rdudevdebug && echo 'udev_log="debug"' >> /etc/udev/udev.conf + +source_conf /etc/conf.d + +# Get the "root=" parameter from the kernel command line, but differentiate +# between the case where it was set to the empty string and the case where it +# wasn't specified at all. +if ! root="$(getarg root=)"; then + root_unset='UNSET' +fi + +rflags="$(getarg rootflags=)" +getargbool 0 ro && rflags="${rflags},ro" +getargbool 0 rw && rflags="${rflags},rw" +rflags="${rflags#,}" + +fstype="$(getarg rootfstype=)" +if [ -z "$fstype" ]; then + fstype="auto" +fi + +export root +export rflags +export fstype + +make_trace_mem "hook cmdline" '1+:mem' '1+:iomem' '3+:slab' +# run scriptlets to parse the command line +getarg 'rd.break=cmdline' -d 'rdbreak=cmdline' && emergency_shell -n cmdline "Break before cmdline" +source_hook cmdline + +[ -f /lib/dracut/parse-resume.sh ] && . /lib/dracut/parse-resume.sh + +case "${root#block:}${root_unset}" in + LABEL=* | UUID=* | PARTUUID=* | PARTLABEL=*) + root="block:$(label_uuid_to_dev "${root#block:}")" + rootok=1 + ;; + /dev/*) + root="block:${root#block:}" + rootok=1 + ;; + UNSET | gpt-auto | tmpfs) + # systemd's gpt-auto-generator/fstab-generator handles this case. + rootok=1 + ;; +esac + +[ -z "${root}${root_unset}" ] && die "Empty root= argument" +[ -z "$rootok" ] && die "Don't know how to handle 'root=$root'" + +export root rflags fstype netroot NEWROOT + +export -p > /dracut-state.sh + +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-emergency.service b/modules.d/98dracut-systemd/dracut-emergency.service new file mode 100644 index 0000000..eb3e67f --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-emergency.service @@ -0,0 +1,28 @@ +# This file is part of dracut. + +[Unit] +Description=Dracut Emergency Shell +Documentation=man:dracut.bootup(7) +DefaultDependencies=no +After=systemd-vconsole-setup.service +Wants=systemd-vconsole-setup.service +Conflicts=shutdown.target emergency.target + +[Service] +Environment=HOME=/ +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +WorkingDirectory=/ +ExecStart=-/bin/dracut-emergency +ExecStopPost=-/bin/rm -f -- /.console_lock +Type=oneshot +StandardInput=tty-force +StandardOutput=inherit +StandardError=inherit +KillMode=process +IgnoreSIGPIPE=no +TasksMax=infinity + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-emergency.sh b/modules.d/98dracut-systemd/dracut-emergency.sh new file mode 100755 index 0000000..c6637a5 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-emergency.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +export DRACUT_SYSTEMD=1 +if [ -f /dracut-state.sh ]; then + . /dracut-state.sh 2> /dev/null +fi +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +source_conf /etc/conf.d + +type plymouth > /dev/null 2>&1 && plymouth quit + +export _rdshell_name="dracut" action="Boot" hook="emergency" +_emergency_action=$(getarg rd.emergency) + +if getargbool 1 rd.shell -d -y rdshell || getarg rd.break -d rdbreak; then + FSTXT="/run/dracut/fsck/fsck_help_$fstype.txt" + RDSOSREPORT="$(rdsosreport)" + source_hook "$hook" + while read -r _tty rest; do + ( + echo + echo "$RDSOSREPORT" + echo + echo + echo 'Entering emergency mode. Exit the shell to continue.' + echo 'Type "journalctl" to view system logs.' + echo 'You might want to save "/run/initramfs/rdsosreport.txt" to a USB stick or /boot' + echo 'after mounting them and attach it to a bug report.' + echo + echo + [ -f "$FSTXT" ] && cat "$FSTXT" + ) > /dev/"$_tty" + done < /proc/consoles + [ -f /etc/profile ] && . /etc/profile + [ -z "$PS1" ] && export PS1="$_name:\${PWD}# " + exec sulogin -e +else + export hook="shutdown-emergency" + warn "$action has failed. To debug this issue add \"rd.shell rd.debug\" to the kernel command line." + source_hook "$hook" + [ -z "$_emergency_action" ] && _emergency_action=halt +fi + +/bin/rm -f -- /.console_lock + +case "$_emergency_action" in + reboot) + reboot || exit 1 + ;; + poweroff) + poweroff || exit 1 + ;; + halt) + halt || exit 1 + ;; +esac + +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-initqueue.service b/modules.d/98dracut-systemd/dracut-initqueue.service new file mode 100644 index 0000000..4cd32eb --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-initqueue.service @@ -0,0 +1,28 @@ +# This file is part of dracut. + +[Unit] +Description=dracut initqueue hook +Documentation=man:dracut-initqueue.service(8) man:dracut.bootup(7) +DefaultDependencies=no +Before=remote-fs-pre.target +Wants=remote-fs-pre.target +After=systemd-udev-trigger.service +Wants=systemd-udev-trigger.service +ConditionPathExists=/usr/lib/initrd-release +ConditionPathExists=|/lib/dracut/need-initqueue +ConditionKernelCommandLine=|rd.break=initqueue +Conflicts=shutdown.target emergency.target + +[Service] +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +Type=oneshot +ExecStart=-/bin/dracut-initqueue +StandardInput=null +StandardError=journal+console +KillMode=process +RemainAfterExit=yes + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-initqueue.service.8.asc b/modules.d/98dracut-systemd/dracut-initqueue.service.8.asc new file mode 100644 index 0000000..66f2d33 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-initqueue.service.8.asc @@ -0,0 +1,25 @@ +DRACUT-INITQUEUE.SERVICE(8) +=========================== +:doctype: manpage +:man source: dracut +:man manual: dracut + +NAME +---- +dracut-initqueue.service - runs the dracut main loop to find the real root + +SYNOPSIS +-------- +dracut-initqueue.service + +DESCRIPTION +----------- +This service runs all the main loop of dracut in the initramfs to find the real root. + +AUTHORS +------- +Harald Hoyer + +SEE ALSO +-------- +*dracut.bootup*(7) *dracut*(8) diff --git a/modules.d/98dracut-systemd/dracut-initqueue.sh b/modules.d/98dracut-systemd/dracut-initqueue.sh new file mode 100755 index 0000000..ce919ce --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-initqueue.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +export DRACUT_SYSTEMD=1 +if [ -f /dracut-state.sh ]; then + . /dracut-state.sh 2> /dev/null +fi +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +source_conf /etc/conf.d + +make_trace_mem "hook initqueue" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=initqueue' -d 'rdbreak=initqueue' && emergency_shell -n initqueue "Break before initqueue" + +RDRETRY=$(getarg rd.retry -d 'rd_retry=') +RDRETRY=${RDRETRY:-180} +RDRETRY=$((RDRETRY * 2)) +export RDRETRY + +main_loop=0 +export main_loop + +while :; do + + check_finished && break + + udevadm settle --exit-if-exists="$hookdir"/initqueue/work + + check_finished && break + + if [ -f "$hookdir"/initqueue/work ]; then + rm -f -- "$hookdir/initqueue/work" + fi + + for job in "$hookdir"/initqueue/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + check_finished && break 2 + done + + udevadm settle --timeout=0 > /dev/null 2>&1 || continue + + for job in "$hookdir"/initqueue/settled/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + check_finished && break 2 + done + + udevadm settle --timeout=0 > /dev/null 2>&1 || continue + + # no more udev jobs and queues empty. + sleep 0.5 + + for i in /run/systemd/ask-password/ask.*; do + [ -e "$i" ] && continue 2 + done + + if [ $main_loop -gt $((2 * RDRETRY / 3)) ]; then + warn "dracut-initqueue: timeout, still waiting for following initqueue hooks:" + for _f in "$hookdir"/initqueue/finished/*.sh; do + warn "$_f: \"$(cat "$_f")\"" + done + if [ "$(ls -A "$hookdir"/initqueue/finished)" ]; then + warn "dracut-initqueue: starting timeout scripts" + for job in "$hookdir"/initqueue/timeout/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + udevadm settle --timeout=0 > /dev/null 2>&1 || main_loop=0 + [ -f "$hookdir"/initqueue/work ] && main_loop=0 + [ $main_loop -eq 0 ] && break + done + fi + fi + + main_loop=$((main_loop + 1)) + if [ $main_loop -gt $RDRETRY ]; then + if ! [ -f /sysroot/etc/fstab ] || ! [ -e /sysroot/sbin/init ]; then + emergency_shell "Could not boot." + fi + warn "Not all disks have been found." + warn "You might want to regenerate your initramfs." + break + fi +done + +unset job +unset queuetriggered +unset main_loop +unset RDRETRY + +export -p > /dracut-state.sh + +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-mount.service b/modules.d/98dracut-systemd/dracut-mount.service new file mode 100644 index 0000000..27fff82 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-mount.service @@ -0,0 +1,26 @@ +# This file is part of dracut. + +[Unit] +Description=dracut mount hook +Documentation=man:dracut-mount.service(8) man:dracut.bootup(7) +After=initrd-root-fs.target initrd-parse-etc.service +After=dracut-initqueue.service dracut-pre-mount.service +ConditionPathExists=/usr/lib/initrd-release +ConditionDirectoryNotEmpty=|/lib/dracut/hooks/mount +ConditionKernelCommandLine=|rd.break=mount +DefaultDependencies=no +Conflicts=shutdown.target emergency.target + +[Service] +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +Type=oneshot +ExecStart=-/bin/dracut-mount +StandardInput=null +StandardError=journal+console +KillMode=process +RemainAfterExit=yes + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-mount.service.8.asc b/modules.d/98dracut-systemd/dracut-mount.service.8.asc new file mode 100644 index 0000000..969123f --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-mount.service.8.asc @@ -0,0 +1,25 @@ +DRACUT-MOUNT.SERVICE(8) +======================= +:doctype: manpage +:man source: dracut +:man manual: dracut + +NAME +---- +dracut-mount.service - runs the dracut hooks after /sysroot is mounted + +SYNOPSIS +-------- +dracut-mount.service + +DESCRIPTION +----------- +This service runs all dracut hooks after the real root is mounted on /sysroot. + +AUTHORS +------- +Harald Hoyer + +SEE ALSO +-------- +*dracut.bootup*(7) *dracut*(8) diff --git a/modules.d/98dracut-systemd/dracut-mount.sh b/modules.d/98dracut-systemd/dracut-mount.sh new file mode 100755 index 0000000..7892941 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-mount.sh @@ -0,0 +1,38 @@ +#!/bin/sh +export DRACUT_SYSTEMD=1 +if [ -f /dracut-state.sh ]; then + . /dracut-state.sh 2> /dev/null +fi +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +source_conf /etc/conf.d + +make_trace_mem "hook mount" '1:shortmem' '2+:mem' '3+:slab' + +getarg 'rd.break=mount' -d 'rdbreak=mount' && emergency_shell -n mount "Break before mount" +# mount scripts actually try to mount the root filesystem, and may +# be sourced any number of times. As soon as one succeeds, no more are sourced. +i=0 +while :; do + if ismounted "$NEWROOT"; then + usable_root "$NEWROOT" && break + umount "$NEWROOT" + fi + for f in "$hookdir"/mount/*.sh; do + # shellcheck disable=SC1090 + [ -f "$f" ] && . "$f" + if ismounted "$NEWROOT"; then + usable_root "$NEWROOT" && break + warn "$NEWROOT has no proper rootfs layout, ignoring and removing offending mount hook" + umount "$NEWROOT" + rm -f -- "$f" + fi + done + + i=$((i + 1)) + [ $i -gt 20 ] && emergency_shell "Can't mount root filesystem" +done + +export -p > /dracut-state.sh + +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-pre-mount.service b/modules.d/98dracut-systemd/dracut-pre-mount.service new file mode 100644 index 0000000..351370b --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-mount.service @@ -0,0 +1,26 @@ +# This file is part of dracut. + +[Unit] +Description=dracut pre-mount hook +Documentation=man:dracut-pre-mount.service(8) man:dracut.bootup(7) +DefaultDependencies=no +Before=initrd-root-fs.target sysroot.mount systemd-fsck-root.service +After=dracut-initqueue.service cryptsetup.target +ConditionPathExists=/usr/lib/initrd-release +ConditionDirectoryNotEmpty=|/lib/dracut/hooks/pre-mount +ConditionKernelCommandLine=|rd.break=pre-mount +Conflicts=shutdown.target emergency.target + +[Service] +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +Type=oneshot +ExecStart=-/bin/dracut-pre-mount +StandardInput=null +StandardError=journal+console +KillMode=process +RemainAfterExit=yes + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-pre-mount.service.8.asc b/modules.d/98dracut-systemd/dracut-pre-mount.service.8.asc new file mode 100644 index 0000000..9aa671a --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-mount.service.8.asc @@ -0,0 +1,25 @@ +DRACUT-PRE-MOUNT.SERVICE(8) +=========================== +:doctype: manpage +:man source: dracut +:man manual: dracut + +NAME +---- +dracut-pre-mount.service - runs the dracut hooks before /sysroot is mounted + +SYNOPSIS +-------- +dracut-pre-mount.service + +DESCRIPTION +----------- +This service runs all dracut hooks before the real root is mounted on /sysroot. + +AUTHORS +------- +Harald Hoyer + +SEE ALSO +-------- +*dracut.bootup*(7) *dracut*(8) diff --git a/modules.d/98dracut-systemd/dracut-pre-mount.sh b/modules.d/98dracut-systemd/dracut-pre-mount.sh new file mode 100755 index 0000000..ee51605 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-mount.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +export DRACUT_SYSTEMD=1 +if [ -f /dracut-state.sh ]; then + . /dracut-state.sh 2> /dev/null +fi +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +source_conf /etc/conf.d + +make_trace_mem "hook pre-mount" '1:shortmem' '2+:mem' '3+:slab' +# pre pivot scripts are sourced just before we doing cleanup and switch over +# to the new root. +getarg 'rd.break=pre-mount' 'rdbreak=pre-mount' && emergency_shell -n pre-mount "Break before pre-mount" +source_hook pre-mount + +export -p > /dracut-state.sh + +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-pre-pivot.service b/modules.d/98dracut-systemd/dracut-pre-pivot.service new file mode 100644 index 0000000..04a3705 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-pivot.service @@ -0,0 +1,35 @@ +# This file is part of dracut. + +[Unit] +Description=dracut pre-pivot and cleanup hook +Documentation=man:dracut-pre-pivot.service(8) man:dracut.bootup(7) +DefaultDependencies=no +After=initrd.target initrd-parse-etc.service sysroot.mount +After=dracut-initqueue.service dracut-pre-mount.service dracut-mount.service +Before=initrd-cleanup.service +Wants=remote-fs.target +After=remote-fs.target +ConditionPathExists=/usr/lib/initrd-release +ConditionDirectoryNotEmpty=|/lib/dracut/hooks/pre-pivot +ConditionDirectoryNotEmpty=|/lib/dracut/hooks/cleanup +ConditionKernelCommandLine=|rd.break=pre-pivot +ConditionKernelCommandLine=|rd.break=cleanup +ConditionKernelCommandLine=|rd.break +ConditionPathExists=|/dev/root +ConditionPathExists=|/dev/nfs +Conflicts=shutdown.target emergency.target + +[Service] +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +Type=oneshot +ExecStart=-/bin/dracut-pre-pivot +StandardInput=null +StandardError=journal+console +KillMode=process +RemainAfterExit=yes +KeyringMode=shared + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-pre-pivot.service.8.asc b/modules.d/98dracut-systemd/dracut-pre-pivot.service.8.asc new file mode 100644 index 0000000..df500ac --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-pivot.service.8.asc @@ -0,0 +1,25 @@ +DRACUT-PRE-PIVOT.SERVICE(8) +=========================== +:doctype: manpage +:man source: dracut +:man manual: dracut + +NAME +---- +dracut-pre-pivot.service - runs the dracut hooks before switching root + +SYNOPSIS +-------- +dracut-pre-pivot.service + +DESCRIPTION +----------- +This service runs all dracut hooks before the system switched to the real root. + +AUTHORS +------- +Harald Hoyer + +SEE ALSO +-------- +*dracut.bootup*(7) *dracut*(8) diff --git a/modules.d/98dracut-systemd/dracut-pre-pivot.sh b/modules.d/98dracut-systemd/dracut-pre-pivot.sh new file mode 100755 index 0000000..8bf2325 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-pivot.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +export DRACUT_SYSTEMD=1 +if [ -f /dracut-state.sh ]; then + . /dracut-state.sh 2> /dev/null +fi +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +source_conf /etc/conf.d + +make_trace_mem "hook pre-pivot" '1:shortmem' '2+:mem' '3+:slab' +# pre pivot scripts are sourced just before we doing cleanup and switch over +# to the new root. +getarg 'rd.break=pre-pivot' 'rdbreak=pre-pivot' && emergency_shell -n pre-pivot "Break before pre-pivot" +source_hook pre-pivot + +# pre pivot cleanup scripts are sourced just before we switch over to the new root. +getarg 'rd.break=cleanup' 'rdbreak=cleanup' && emergency_shell -n cleanup "Break before cleanup" +source_hook cleanup + +_bv=$(getarg rd.break -d rdbreak) && [ -z "$_bv" ] \ + && emergency_shell -n switch_root "Break before switch_root" +unset _bv + +# remove helper symlink +[ -h /dev/root ] && rm -f -- /dev/root +[ -h /dev/nfs ] && rm -f -- /dev/nfs + +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-pre-trigger.service b/modules.d/98dracut-systemd/dracut-pre-trigger.service new file mode 100644 index 0000000..374cd3a --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-trigger.service @@ -0,0 +1,27 @@ +# This file is part of dracut. + +[Unit] +Description=dracut pre-trigger hook +Documentation=man:dracut-pre-trigger.service(8) man:dracut.bootup(7) +DefaultDependencies=no +Before=systemd-udev-trigger.service dracut-initqueue.service +After=dracut-pre-udev.service systemd-udevd.service systemd-tmpfiles-setup-dev.service +Wants=dracut-pre-udev.service systemd-udevd.service +ConditionPathExists=/usr/lib/initrd-release +ConditionDirectoryNotEmpty=|/lib/dracut/hooks/pre-trigger +ConditionKernelCommandLine=|rd.break=pre-trigger +Conflicts=shutdown.target emergency.target + +[Service] +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +Type=oneshot +ExecStart=-/bin/dracut-pre-trigger +StandardInput=null +StandardError=journal+console +KillMode=process +RemainAfterExit=yes + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-pre-trigger.service.8.asc b/modules.d/98dracut-systemd/dracut-pre-trigger.service.8.asc new file mode 100644 index 0000000..5b0cc7f --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-trigger.service.8.asc @@ -0,0 +1,25 @@ +DRACUT-PRE-TRIGGER.SERVICE(8) +============================= +:doctype: manpage +:man source: dracut +:man manual: dracut + +NAME +---- +dracut-pre-trigger.service - runs the dracut hooks before udevd is triggered + +SYNOPSIS +-------- +dracut-pre-trigger.service + +DESCRIPTION +----------- +This service runs all dracut hooks before udevd is triggered in the initramfs. + +AUTHORS +------- +Harald Hoyer + +SEE ALSO +-------- +*dracut.bootup*(7) *dracut*(8) diff --git a/modules.d/98dracut-systemd/dracut-pre-trigger.sh b/modules.d/98dracut-systemd/dracut-pre-trigger.sh new file mode 100755 index 0000000..dd0215e --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-trigger.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +export DRACUT_SYSTEMD=1 +if [ -f /dracut-state.sh ]; then + . /dracut-state.sh 2> /dev/null +fi +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +source_conf /etc/conf.d + +make_trace_mem "hook pre-trigger" '1:shortmem' '2+:mem' '3+:slab' + +source_hook pre-trigger + +getarg 'rd.break=pre-trigger' 'rdbreak=pre-trigger' && emergency_shell -n pre-trigger "Break before pre-trigger" + +udevadm control --reload > /dev/null 2>&1 || : + +export -p > /dracut-state.sh + +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-pre-udev.service b/modules.d/98dracut-systemd/dracut-pre-udev.service new file mode 100644 index 0000000..a61e9d4 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-udev.service @@ -0,0 +1,31 @@ +# This file is part of dracut. + +[Unit] +Description=dracut pre-udev hook +Documentation=man:dracut-pre-udev.service(8) man:dracut.bootup(7) +DefaultDependencies=no +Before=systemd-udevd.service dracut-pre-trigger.service +After=dracut-cmdline.service +Wants=dracut-cmdline.service +ConditionPathExists=/usr/lib/initrd-release +ConditionDirectoryNotEmpty=|/lib/dracut/hooks/pre-udev +ConditionKernelCommandLine=|rd.break=pre-udev +ConditionKernelCommandLine=|rd.driver.blacklist +ConditionKernelCommandLine=|rd.driver.pre +ConditionKernelCommandLine=|rd.driver.post +ConditionPathExistsGlob=|/etc/cmdline.d/*.conf +Conflicts=shutdown.target emergency.target + +[Service] +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +Type=oneshot +ExecStart=-/bin/dracut-pre-udev +StandardInput=null +StandardError=journal+console +KillMode=process +RemainAfterExit=yes + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/dracut-pre-udev.service.8.asc b/modules.d/98dracut-systemd/dracut-pre-udev.service.8.asc new file mode 100644 index 0000000..c91c8e2 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-udev.service.8.asc @@ -0,0 +1,25 @@ +DRACUT-PRE-UDEV.SERVICE(8) +========================== +:doctype: manpage +:man source: dracut +:man manual: dracut + +NAME +---- +dracut-pre-udev.service - runs the dracut hooks before udevd is started + +SYNOPSIS +-------- +dracut-pre-udev.service + +DESCRIPTION +----------- +This service runs all dracut hooks before udevd is started in the initramfs. + +AUTHORS +------- +Harald Hoyer + +SEE ALSO +-------- +*dracut.bootup*(7) *dracut*(8) diff --git a/modules.d/98dracut-systemd/dracut-pre-udev.sh b/modules.d/98dracut-systemd/dracut-pre-udev.sh new file mode 100755 index 0000000..feab32c --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-pre-udev.sh @@ -0,0 +1,55 @@ +#!/bin/sh +export DRACUT_SYSTEMD=1 +if [ -f /dracut-state.sh ]; then + . /dracut-state.sh 2> /dev/null +fi +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +source_conf /etc/conf.d + +make_trace_mem "hook pre-udev" '1:shortmem' '2+:mem' '3+:slab' +# pre pivot scripts are sourced just before we doing cleanup and switch over +# to the new root. +getarg 'rd.break=pre-udev' 'rdbreak=pre-udev' && emergency_shell -n pre-udev "Break before pre-udev" +source_hook pre-udev + +_modprobe_d=/etc/modprobe.d +if [ -d /usr/lib/modprobe.d ]; then + _modprobe_d=/usr/lib/modprobe.d +elif [ -d /lib/modprobe.d ]; then + _modprobe_d=/lib/modprobe.d +elif [ ! -d $_modprobe_d ]; then + mkdir -p $_modprobe_d +fi + +for i in $(getargs rd.driver.pre -d rdloaddriver=); do + ( + IFS=, + for p in $i; do + modprobe "$p" 2>&1 | vinfo + done + ) +done + +[ -d /etc/modprobe.d ] || mkdir -p /etc/modprobe.d + +for i in $(getargs rd.driver.blacklist -d rdblacklist=); do + ( + IFS=, + for p in $i; do + echo "blacklist $p" >> $_modprobe_d/initramfsblacklist.conf + done + ) +done + +for p in $(getargs rd.driver.post -d rdinsmodpost=); do + echo "blacklist $p" >> $_modprobe_d/initramfsblacklist.conf + _do_insmodpost=1 +done + +[ -n "$_do_insmodpost" ] && initqueue --settled --unique --onetime insmodpost.sh +unset _do_insmodpost _modprobe_d +unset i + +export -p > /dracut-state.sh +exit 0 diff --git a/modules.d/98dracut-systemd/dracut-shutdown-onfailure.service b/modules.d/98dracut-systemd/dracut-shutdown-onfailure.service new file mode 100644 index 0000000..0570535 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-shutdown-onfailure.service @@ -0,0 +1,11 @@ +# This file is part of dracut. + +[Unit] +Description=Service executing upon dracut-shutdown failure to perform cleanup +Documentation=man:dracut-shutdown.service(8) +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=-/bin/rm /run/initramfs/shutdown +StandardError=null diff --git a/modules.d/98dracut-systemd/dracut-shutdown.service b/modules.d/98dracut-systemd/dracut-shutdown.service new file mode 100644 index 0000000..b2b704a --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-shutdown.service @@ -0,0 +1,15 @@ +# This file is part of dracut. + +[Unit] +Description=Restore /run/initramfs on shutdown +Documentation=man:dracut-shutdown.service(8) +After=local-fs.target boot.mount boot.automount +Wants=local-fs.target +ConditionPathExists=!/run/initramfs/bin/sh +OnFailure=dracut-shutdown-onfailure.service + +[Service] +RemainAfterExit=yes +Type=oneshot +ExecStart=/bin/true +ExecStop=/usr/lib/dracut/dracut-initramfs-restore diff --git a/modules.d/98dracut-systemd/dracut-shutdown.service.8.asc b/modules.d/98dracut-systemd/dracut-shutdown.service.8.asc new file mode 100644 index 0000000..21ec88c --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-shutdown.service.8.asc @@ -0,0 +1,53 @@ +DRACUT-SHUTDOWN.SERVICE(8) +=========================== +:doctype: manpage +:man source: dracut +:man manual: dracut + +NAME +---- +dracut-shutdown.service - unpack the initramfs to /run/initramfs + +SYNOPSIS +-------- +dracut-shutdown.service + +DESCRIPTION +----------- +This service unpacks the initramfs image to /run/initramfs. +systemd pivots into /run/initramfs at shutdown, so the root filesystem +can be safely unmounted. + +The following steps are executed during a shutdown: + +* systemd switches to the shutdown.target +* systemd starts /lib/systemd/system/shutdown.target.wants/dracut-shutdown.service +* dracut-shutdown.service executes /usr/lib/dracut/dracut-initramfs-restore which unpacks the initramfs to /run/initramfs +* systemd finishes shutdown.target +* systemd kills all processes +* systemd tries to unmount everything and mounts the remaining read-only +* systemd checks, if there is a /run/initramfs/shutdown executable +* if yes, it does a pivot_root to /run/initramfs and executes ./shutdown. The old root is then mounted on /oldroot. /usr/lib/dracut/modules.d/99shutdown/shutdown.sh is the shutdown executable. +* shutdown will try to umount every /oldroot mount and calls the various shutdown hooks from the dracut modules + +This ensures, that all devices are disassembled and unmounted cleanly. + +To debug the shutdown process, you can get a shell in the shutdown procedure +by injecting "rd.break=pre-shutdown rd.shell" or "rd.break=shutdown rd.shell". +---- +# mkdir -p /run/initramfs/etc/cmdline.d +# echo "rd.break=pre-shutdown rd.shell" > /run/initramfs/etc/cmdline.d/debug.conf +# touch /run/initramfs/.need_shutdown +---- + +In case the unpack of the initramfs fails, dracut-shutdown-onfailure.service +executes to make sure switch root doesn't happen, since it would result in +switching to an incomplete initramfs. + +AUTHORS +------- +Harald Hoyer + +SEE ALSO +-------- +*dracut*(8) diff --git a/modules.d/98dracut-systemd/dracut-tmpfiles.conf b/modules.d/98dracut-systemd/dracut-tmpfiles.conf new file mode 100644 index 0000000..3c21ce8 --- /dev/null +++ b/modules.d/98dracut-systemd/dracut-tmpfiles.conf @@ -0,0 +1,3 @@ +d /run/initramfs 0755 root root - +d /run/initramfs/log 0755 root root - +L /var/log - - - - ../run/initramfs/log diff --git a/modules.d/98dracut-systemd/emergency.service b/modules.d/98dracut-systemd/emergency.service new file mode 100644 index 0000000..27c69e1 --- /dev/null +++ b/modules.d/98dracut-systemd/emergency.service @@ -0,0 +1,29 @@ +# This file is part of dracut. + +[Unit] +Description=Emergency Shell +Documentation=man:dracut.bootup(7) +DefaultDependencies=no +After=systemd-vconsole-setup.service +Wants=systemd-vconsole-setup.service +Conflicts=shutdown.target +Before=shutdown.target + +[Service] +Environment=HOME=/ +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +WorkingDirectory=/ +ExecStart=/bin/dracut-emergency +ExecStopPost=-/usr/bin/systemctl --fail --no-block default +Type=idle +StandardInput=tty-force +StandardOutput=inherit +StandardError=inherit +KillMode=process +IgnoreSIGPIPE=no +TasksMax=infinity + +# Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash +# terminates cleanly. +KillSignal=SIGHUP diff --git a/modules.d/98dracut-systemd/module-setup.sh b/modules.d/98dracut-systemd/module-setup.sh new file mode 100755 index 0000000..3195377 --- /dev/null +++ b/modules.d/98dracut-systemd/module-setup.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# called by dracut +check() { + [[ $mount_needs ]] && return 1 + + return 0 +} + +# called by dracut +depends() { + echo "systemd-initrd" + return 0 +} + +installkernel() { + return 0 +} + +# called by dracut +install() { + inst_script "$moddir/dracut-emergency.sh" /bin/dracut-emergency + inst_simple "$moddir/emergency.service" "${systemdsystemunitdir}"/emergency.service + inst_simple "$moddir/dracut-emergency.service" "${systemdsystemunitdir}"/dracut-emergency.service + inst_simple "$moddir/emergency.service" "${systemdsystemunitdir}"/rescue.service + + ln_r "${systemdsystemunitdir}/initrd.target" "${systemdsystemunitdir}/default.target" + + inst_script "$moddir/dracut-cmdline.sh" /bin/dracut-cmdline + inst_script "$moddir/dracut-cmdline-ask.sh" /bin/dracut-cmdline-ask + inst_script "$moddir/dracut-pre-udev.sh" /bin/dracut-pre-udev + inst_script "$moddir/dracut-pre-trigger.sh" /bin/dracut-pre-trigger + inst_script "$moddir/dracut-initqueue.sh" /bin/dracut-initqueue + inst_script "$moddir/dracut-pre-mount.sh" /bin/dracut-pre-mount + inst_script "$moddir/dracut-mount.sh" /bin/dracut-mount + inst_script "$moddir/dracut-pre-pivot.sh" /bin/dracut-pre-pivot + + inst_script "$moddir/rootfs-generator.sh" "$systemdutildir"/system-generators/dracut-rootfs-generator + + inst_hook cmdline 00 "$moddir/parse-root.sh" + + for i in \ + dracut-cmdline.service \ + dracut-cmdline-ask.service \ + dracut-initqueue.service \ + dracut-mount.service \ + dracut-pre-mount.service \ + dracut-pre-pivot.service \ + dracut-pre-trigger.service \ + dracut-pre-udev.service; do + inst_simple "$moddir/${i}" "$systemdsystemunitdir/${i}" + $SYSTEMCTL -q --root "$initdir" add-wants initrd.target "$i" + done + + inst_simple "$moddir/dracut-tmpfiles.conf" "$tmpfilesdir/dracut-tmpfiles.conf" + + inst_multiple sulogin +} diff --git a/modules.d/98dracut-systemd/parse-root.sh b/modules.d/98dracut-systemd/parse-root.sh new file mode 100755 index 0000000..90f145a --- /dev/null +++ b/modules.d/98dracut-systemd/parse-root.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +root=$(getarg root=) +case "${root#block:}" in + LABEL=* | UUID=* | PARTUUID=* | PARTLABEL=*) + root="block:$(label_uuid_to_dev "$root")" + rootok=1 + ;; + /dev/nfs | /dev/root) # ignore legacy + ;; + /dev/*) + root="block:${root}" + rootok=1 + ;; +esac + +if [ "$rootok" = "1" ]; then + root_dev="${root#block:}" + root_name="$(str_replace "$root_dev" '/' '\x2f')" + if ! [ -e "$hookdir/initqueue/finished/devexists-${root_name}.sh" ]; then + + # If a LUKS device needs unlocking via systemd in the initrd, assume + # it's for the root device. In that case, don't block on it if it's + # after remote-fs-pre.target since the initqueue is ordered before it so + # it will never actually show up (think Tang-pinned rootfs). + cat > "$hookdir/initqueue/finished/devexists-${root_name}.sh" << EOF +if ! grep -q After=remote-fs-pre.target /run/systemd/generator/systemd-cryptsetup@*.service 2>/dev/null; then + [ -e "$root_dev" ] +fi +EOF + { + printf '[ -e "%s" ] || ' "$root_dev" + printf 'warn "\"%s\" does not exist"\n' "$root_dev" + } >> "$hookdir/emergency/80-${root_name}.sh" + fi +fi diff --git a/modules.d/98dracut-systemd/rootfs-generator.sh b/modules.d/98dracut-systemd/rootfs-generator.sh new file mode 100755 index 0000000..cef3f49 --- /dev/null +++ b/modules.d/98dracut-systemd/rootfs-generator.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +generator_wait_for_dev() { + local _name + local _timeout + + _name=$(dev_unit_name "$1") + _timeout=$(getarg rd.timeout) + _timeout=${_timeout:-0} + + if ! [ -L "$GENERATOR_DIR"/initrd.target.wants/"${_name}".device ]; then + [ -d "$GENERATOR_DIR"/initrd.target.wants ] || mkdir -p "$GENERATOR_DIR"/initrd.target.wants + ln -s ../"${_name}".device "$GENERATOR_DIR"/initrd.target.wants/"${_name}".device + fi + + if ! [ -f "$GENERATOR_DIR"/"${_name}".device.d/timeout.conf ]; then + mkdir -p "$GENERATOR_DIR"/"${_name}".device.d + { + echo "[Unit]" + echo "JobTimeoutSec=$_timeout" + echo "JobRunningTimeoutSec=$_timeout" + } > "$GENERATOR_DIR"/"${_name}".device.d/timeout.conf + fi +} + +generator_mount_rootfs() { + local _type="$2" + local _flags="$3" + local _name + + [ -z "$1" ] && return 0 + + _name=$(dev_unit_name "$1") + if ! [ -f "$GENERATOR_DIR"/sysroot.mount ]; then + { + echo "[Unit]" + echo "Before=initrd-root-fs.target" + echo "Requires=systemd-fsck@${_name}.service" + echo "After=systemd-fsck@${_name}.service" + echo "[Mount]" + echo "Where=/sysroot" + echo "What=$1" + echo "Options=${_flags}" + echo "Type=${_type}" + } > "$GENERATOR_DIR"/sysroot.mount + fi + if ! [ -L "$GENERATOR_DIR"/initrd-root-fs.target.requires/sysroot.mount ]; then + [ -d "$GENERATOR_DIR"/initrd-root-fs.target.requires ] || mkdir -p "$GENERATOR_DIR"/initrd-root-fs.target.requires + ln -s ../sysroot.mount "$GENERATOR_DIR"/initrd-root-fs.target.requires/sysroot.mount + fi +} + +generator_fsck_after_pre_mount() { + local _name + + [ -z "$1" ] && return 0 + + _name=$(dev_unit_name "$1") + [ -d "$GENERATOR_DIR"/systemd-fsck@"${_name}".service.d ] || mkdir -p "$GENERATOR_DIR"/systemd-fsck@"${_name}".service.d + if ! [ -f "$GENERATOR_DIR"/systemd-fsck@"${_name}".service.d/after-pre-mount.conf ]; then + { + echo "[Unit]" + echo "After=dracut-pre-mount.service" + } > "$GENERATOR_DIR"/systemd-fsck@"${_name}".service.d/after-pre-mount.conf + fi + +} + +root=$(getarg root=) +case "${root#block:}" in + LABEL=* | UUID=* | PARTUUID=* | PARTLABEL=*) + root="block:$(label_uuid_to_dev "$root")" + rootok=1 + ;; + /dev/nfs) # ignore legacy /dev/nfs + ;; + /dev/*) + root="block:${root}" + rootok=1 + ;; +esac + +if [ "$rootok" = "1" ]; then + GENERATOR_DIR="$1" + [ -z "$GENERATOR_DIR" ] && exit 1 + [ -d "$GENERATOR_DIR" ] || mkdir -p "$GENERATOR_DIR" + + generator_wait_for_dev "${root#block:}" + generator_fsck_after_pre_mount "${root#block:}" + strstr "$(cat /proc/cmdline)" 'root=' || generator_mount_rootfs "${root#block:}" "$(getarg rootfstype=)" "$(getarg rootflags=)" +fi + +exit 0 diff --git a/modules.d/98ecryptfs/README b/modules.d/98ecryptfs/README new file mode 100644 index 0000000..f741c54 --- /dev/null +++ b/modules.d/98ecryptfs/README @@ -0,0 +1,50 @@ +# Directions for creating the encrypted key that will be used to mount an +# eCryptfs filesystem + +# Create the eCryptfs key (encrypted key type) +# +# The encrypted key type supports two formats: the 'default' format allows +# to generate a random symmetric key of the length specified, the 'ecryptfs' +# format generates an authentication token for the eCryptfs filesystem, +# which contains a randomly generated key. Two requirements for the latter +# format is that the key description must contain exactly 16 hexadecimal +# characters and that the encrypted key length must be equal to 64. +$ keyctl add encrypted 1000100010001000 "new ecryptfs trusted:kmk-trusted 64" @u +782117972 + +# Save the encrypted key +$ su -c 'keyctl pipe `keyctl search @u encrypted 1000100010001000` > /etc/keys/ecryptfs-trusted.blob' + +# The eCryptfs key path name can be set in one of the following ways (specified in +# the order in which the variable is overwritten): + +1) use the default value: +-------------------------------------------------------------------------- +ECRYPTFSKEY="/etc/keys/ecryptfs-trusted.blob" +-------------------------------------------------------------------------- + +2) create the configuration file '/etc/sysconfig/ecryptfs' and set the ECRYPTFSKEY +variable; + +3) specify the eCryptfs key path name in the 'ecryptfskey=' parameter of the kernel command +line. + +# The configuration file '/etc/sysconfig/ecryptfs' is also used to specify +# more options for mounting the eCryptfs filesystem: + +ECRYPTFSSRCDIR: existent directory in the lower root filesystem; +ECRYPTFSDSTDIR: mount point directory for the eCryptfs filesystem (the directory must be + created in the root filesystem before rebooting the platform); +ECRYPTFS_EXTRA_MOUNT_OPTS: extra mount options for the eCryptfs filesystem (the 'ecryptfs_sig' + option is automatically added by the dracut script). + +# Example of the configuration file: +----------- '/etc/sysconfig/ecryptfs' (with default values) ----------- +ECRYPTFS_KEY="/etc/keys/ecryptfs-trusted.blob" +ECRYPTFSSRCDIR="/secret" +ECRYPTFSDSTDIR="${ECRYPTFSSRCDIR}" +ECRYPTFS_EXTRA_MOUNT_OPTS="" +----------------------------------------------------------------------- + +# If the variable ECRYPTFSDSTDIR is not specified in the configuration file, +# its value will be equal to that of ECRYPTFSSRCDIR. diff --git a/modules.d/98ecryptfs/ecryptfs-mount.sh b/modules.d/98ecryptfs/ecryptfs-mount.sh new file mode 100755 index 0000000..0c1e228 --- /dev/null +++ b/modules.d/98ecryptfs/ecryptfs-mount.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +# Licensed under the GPLv2 +# +# Copyright (C) 2011 Politecnico di Torino, Italy +# TORSEC group -- http://security.polito.it +# Roberto Sassu <roberto.sassu@polito.it> + +ECRYPTFSCONFIG="${NEWROOT}/etc/sysconfig/ecryptfs" +ECRYPTFSKEYTYPE="encrypted" +ECRYPTFSKEYDESC="1000100010001000" +ECRYPTFSKEYID="" +ECRYPTFSSRCDIR="/secret" +ECRYPTFS_EXTRA_MOUNT_OPTS="" + +load_ecryptfs_key() { + # override the eCryptfs key path name from the 'ecryptfskey=' parameter in the kernel + # command line + if ECRYPTFSKEYARG=$(getarg ecryptfskey=); then + ECRYPTFSKEY=${ECRYPTFSKEYARG} + fi + + # set the default value + [ -z "${ECRYPTFSKEY}" ] \ + && ECRYPTFSKEY="/etc/keys/ecryptfs-trusted.blob" + + # set the eCryptfs key path name + ECRYPTFSKEYPATH="${NEWROOT}${ECRYPTFSKEY}" + + # check for eCryptfs encrypted key's existence + if [ ! -f "${ECRYPTFSKEYPATH}" ]; then + if [ "${RD_DEBUG}" = "yes" ]; then + info "eCryptfs: key file not found: ${ECRYPTFSKEYPATH}" + fi + return 1 + fi + + # read the eCryptfs encrypted key blob + read -r KEYBLOB < "${ECRYPTFSKEYPATH}" + + # load the eCryptfs encrypted key blob + if ! ECRYPTFSKEYID=$(keyctl add ${ECRYPTFSKEYTYPE} ${ECRYPTFSKEYDESC} "load ${KEYBLOB}" @u); then + info "eCryptfs: failed to load the eCryptfs key: ${ECRYPTFSKEYDESC}" + return 1 + fi + + return 0 +} + +unload_ecryptfs_key() { + # unlink the eCryptfs encrypted key + keyctl unlink "${ECRYPTFSKEYID}" @u || { + info "eCryptfs: failed to unlink the eCryptfs key: ${ECRYPTFSKEYDESC}" + return 1 + } + + return 0 +} + +mount_ecryptfs() { + # read the configuration from the config file + # shellcheck disable=SC1090 + [ -f "${ECRYPTFSCONFIG}" ] \ + && . "${ECRYPTFSCONFIG}" + + # load the eCryptfs encrypted key + load_ecryptfs_key || return 1 + + # set the default value for ECRYPTFSDSTDIR + [ -z "${ECRYPTFSDSTDIR}" ] \ + && ECRYPTFSDSTDIR=${ECRYPTFSSRCDIR} + + # set the eCryptfs filesystem mount point + ECRYPTFSSRCMNT="${NEWROOT}${ECRYPTFSSRCDIR}" + ECRYPTFSDSTMNT="${NEWROOT}${ECRYPTFSDSTDIR}" + + # build the mount options variable + ECRYPTFS_MOUNT_OPTS="ecryptfs_sig=${ECRYPTFSKEYDESC}" + [ -n "${ECRYPTFS_EXTRA_MOUNT_OPTS}" ] \ + && ECRYPTFS_MOUNT_OPTS="${ECRYPTFS_MOUNT_OPTS},${ECRYPTFS_EXTRA_MOUNT_OPTS}" + + # mount the eCryptfs filesystem + info "Mounting the configured eCryptfs filesystem" + mount -i -t ecryptfs -o${ECRYPTFS_MOUNT_OPTS} "${ECRYPTFSSRCMNT}" "${ECRYPTFSDSTMNT}" > /dev/null || { + info "eCryptfs: mount of the eCryptfs filesystem failed" + return 1 + } + + # unload the eCryptfs encrypted key + unload_ecryptfs_key || return 1 + + return 0 +} + +mount_ecryptfs diff --git a/modules.d/98ecryptfs/module-setup.sh b/modules.d/98ecryptfs/module-setup.sh new file mode 100755 index 0000000..9328b51 --- /dev/null +++ b/modules.d/98ecryptfs/module-setup.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + echo masterkey + return 0 +} + +# called by dracut +installkernel() { + instmods ecryptfs +} + +# called by dracut +install() { + inst_hook pre-pivot 63 "$moddir/ecryptfs-mount.sh" +} diff --git a/modules.d/98integrity/README b/modules.d/98integrity/README new file mode 100644 index 0000000..b16c6b6 --- /dev/null +++ b/modules.d/98integrity/README @@ -0,0 +1,68 @@ +# Directions for creating the encrypted key that will be used to initialize +# the EVM software. + +# Create the EVM key (encrypted key type) +# +# The encrypted key is a random number encrypted/decrypted using the +# kernel master key. The encrypted key is only exposed to userspace +# as an encrypted datablob. +$ keyctl add encrypted evm-key "new trusted:kmk-trusted 32" @u +782117972 + +# Save the encrypted key +$ su -c 'keyctl pipe `keyctl search @u encrypted evm-key` > /etc/keys/evm-trusted.blob' + +# The EVM key path name can be set in one of the following ways (specified in +# the order in which the variable is overwritten): + +1) use the default value: +-------------------------------------------------------------------------- +EVMKEY="/etc/keys/evm-trusted.blob" +-------------------------------------------------------------------------- + +2) create the configuration file '/etc/sysconfig/evm' and set the EVMKEY variable; + +3) specify the EVM key path name in the 'evmkey=' parameter of the kernel command +line. + + +# Directions for loading a custom IMA policy. + +# Write the policy following the instructions provided in the file +# 'Documentation/ABI/testing/ima_policy' of the kernel documentation. + +# Save the policy in a file. + +# Create the configuration file '/etc/sysconfig/ima' to override the path name of +# the IMA custom policy. +------------- '/etc/sysconfig/ima' (with the default value) ------------- +IMAPOLICY="/etc/sysconfig/ima-policy" +------------------------------------------------------------------------- + + +# Information on loading distro, third party or local keys on the trusted IMA keyring + +# Loading distro, third party or local keys on the trusted IMA keyring requires +# creating a local certificate authority(local-CA), installing the local-CA's +# public key on the system-keyring and signing the certificates with the local-CA +# key. +# +# Many directions for creating a mini certificate authority exist on the web +# (eg. openssl, yubikey). (Reminder: safely storing the private key offline is +# really important, especially in the case of the local-CA's private key.) The +# local-CA's public key can be loaded onto the system keyring either by building +# the key into the kernel or, on Fedora, storing it in the UEFI/Mok keyring. (As +# of writing, the patches for loading the UEFI/Mok keys on the system-keyring +# have not been upstreamed.) +# +# To view the system keyring: keyctl show %keyring:.system_keyring +# +# Most on-line directions for signing certificates requires creating a Certificate +# Signing Request (CSR). Creating such a request requires access to the private +# key, which would not be available when signing distro or 3rd party certificates. +# Openssl provides the "-ss_cert" option for directly signing certificates. + +# 98integrity/ima-keys-load.sh script loads the signed certificates stored +# in the $IMAKEYSDIR onto the trusted IMA keyring. The default $IMAKEYSDIR +# directory is /etc/keys/ima, but can be specified in the /etc/sysconfig/ima +# policy. diff --git a/modules.d/98integrity/evm-enable.sh b/modules.d/98integrity/evm-enable.sh new file mode 100755 index 0000000..fbae5f3 --- /dev/null +++ b/modules.d/98integrity/evm-enable.sh @@ -0,0 +1,169 @@ +#!/bin/sh + +# Licensed under the GPLv2 +# +# Copyright (C) 2011 Politecnico di Torino, Italy +# TORSEC group -- http://security.polito.it +# Roberto Sassu <roberto.sassu@polito.it> + +EVMSECFILE="${SECURITYFSDIR}/evm" +EVMCONFIG="${NEWROOT}/etc/sysconfig/evm" +EVMKEYDESC="evm-key" +EVMKEYTYPE="encrypted" +EVMKEYID="" +EVM_ACTIVATION_BITS=0 + +# The following variables can be set in /etc/sysconfig/evm: +# EVMKEY: path to the symmetric key; defaults to /etc/keys/evm-trusted.blob +# EVMKEYDESC: Description of the symmetric key; default is 'evm-key' +# EVMKEYTYPE: Type of the symmetric key; default is 'encrypted' +# EVMX509: path to x509 cert; default is /etc/keys/x509_evm.der +# EVM_ACTIVATION_BITS: additional EVM activation bits, such as +# EVM_SETUP_COMPLETE; default is 0 +# EVMKEYSDIR: Directory with more x509 certs; default is /etc/keys/evm/ + +load_evm_key() { + # read the configuration from the config file + # shellcheck disable=SC1090 + [ -f "${EVMCONFIG}" ] \ + && . "${EVMCONFIG}" + + # override the EVM key path name from the 'evmkey=' parameter in the kernel + # command line + if EVMKEYARG=$(getarg evmkey=); then + EVMKEY=${EVMKEYARG} + fi + + # set the default value + [ -z "${EVMKEY}" ] \ + && EVMKEY="/etc/keys/evm-trusted.blob" + + # set the EVM key path name + EVMKEYPATH="${NEWROOT}${EVMKEY}" + + # check for EVM encrypted key's existence + if [ ! -f "${EVMKEYPATH}" ]; then + if [ "${RD_DEBUG}" = "yes" ]; then + info "integrity: EVM encrypted key file not found: ${EVMKEYPATH}" + fi + return 1 + fi + + # read the EVM encrypted key blob + read -r KEYBLOB < "${EVMKEYPATH}" + + # load the EVM encrypted key + if ! EVMKEYID=$(keyctl add ${EVMKEYTYPE} ${EVMKEYDESC} "load ${KEYBLOB}" @u); then + info "integrity: failed to load the EVM encrypted key: ${EVMKEYDESC}" + return 1 + fi + return 0 +} + +load_evm_x509() { + info "Load EVM IMA X509" + + # override the EVM key path name from the 'evmx509=' parameter in + # the kernel command line + if EVMX509ARG=$(getarg evmx509=); then + EVMX509=${EVMX509ARG} + fi + + # set the default value + [ -z "${EVMX509}" ] \ + && EVMX509="/etc/keys/x509_evm.der" + + # set the EVM public key path name + EVMX509PATH="${NEWROOT}${EVMX509}" + + # check for EVM public key's existence + if [ ! -f "${EVMX509PATH}" ]; then + EVMX509PATH="" + fi + + local evm_pubid line + if line=$(keyctl describe %keyring:.evm); then + # the kernel already setup a trusted .evm keyring so use that one + evm_pubid=${line%%:*} + else + # look for an existing regular keyring + evm_pubid=$(keyctl search @u keyring _evm) + if [ -z "${evm_pubid}" ]; then + # create a new regular _evm keyring + evm_pubid=$(keyctl newring _evm @u) + fi + fi + + if [ -z "${EVMKEYSDIR}" ]; then + EVMKEYSDIR="/etc/keys/evm" + fi + # load the default EVM public key onto the EVM keyring along + # with all the other ones in $EVMKEYSDIR + local key_imported=1 + for PUBKEY in ${EVMX509PATH} "${NEWROOT}${EVMKEYSDIR}"/*; do + if [ ! -f "${PUBKEY}" ]; then + if [ "${RD_DEBUG}" = "yes" ]; then + info "integrity: EVM x509 cert file not found: ${PUBKEY}" + fi + continue + fi + if ! evmctl import "${PUBKEY}" "${evm_pubid}"; then + info "integrity: failed to load the EVM X509 cert ${PUBKEY}" + return 1 + fi + key_imported=0 + done + + if [ "${RD_DEBUG}" = "yes" ]; then + keyctl show @u + fi + + return ${key_imported} +} + +unload_evm_key() { + # unlink the EVM encrypted key + keyctl unlink "${EVMKEYID}" @u || { + info "integrity: failed to unlink the EVM encrypted key: ${EVMKEYDESC}" + return 1 + } + + return 0 +} + +enable_evm() { + # check kernel support for EVM + if [ ! -e "${EVMSECFILE}" ]; then + if [ "${RD_DEBUG}" = "yes" ]; then + info "integrity: EVM kernel support is disabled" + fi + return 0 + fi + + local evm_configured=0 + local EVM_INIT_HMAC=1 EVM_INIT_X509=2 + + # try to load the EVM encrypted key + load_evm_key && evm_configured=${EVM_INIT_HMAC} + + # try to load the EVM public key + load_evm_x509 && evm_configured=$((evm_configured | EVM_INIT_X509)) + + # only enable EVM if a key or x509 certificate could be loaded + if [ $evm_configured -eq 0 ]; then + return 1 + fi + + # initialize EVM + info "Enabling EVM" + echo $((evm_configured | EVM_ACTIVATION_BITS)) > "${EVMSECFILE}" + + if [ "$((evm_configured & EVM_INIT_HMAC))" -ne 0 ]; then + # unload the EVM encrypted key + unload_evm_key || return 1 + fi + + return 0 +} + +enable_evm diff --git a/modules.d/98integrity/ima-keys-load.sh b/modules.d/98integrity/ima-keys-load.sh new file mode 100755 index 0000000..2374550 --- /dev/null +++ b/modules.d/98integrity/ima-keys-load.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +SECURITYFSDIR="/sys/kernel/security" +IMASECDIR="${SECURITYFSDIR}/ima" +IMACONFIG="${NEWROOT}/etc/sysconfig/ima" + +load_x509_keys() { + KEYRING_ID=$1 + + # override the default configuration + if [ -f "${IMACONFIG}" ]; then + # shellcheck disable=SC1090 + . "${IMACONFIG}" + fi + + if [ -z "${IMAKEYSDIR}" ]; then + IMAKEYSDIR="/etc/keys/ima" + fi + + for PUBKEY in "${NEWROOT}${IMAKEYSDIR}"/*; do + # check for public key's existence + if [ ! -f "${PUBKEY}" ]; then + if [ "${RD_DEBUG}" = "yes" ]; then + info "integrity: IMA x509 cert file not found: ${PUBKEY}" + fi + continue + fi + + if ! evmctl import "${PUBKEY}" "${KEYRING_ID}"; then + info "integrity: IMA x509 cert not loaded on keyring: ${PUBKEY}" + fi + done + + if [ "${RD_DEBUG}" = "yes" ]; then + keyctl show "${KEYRING_ID}" + fi + return 0 +} + +# check kernel support for IMA +if [ ! -e "${IMASECDIR}" ]; then + if [ "${RD_DEBUG}" = "yes" ]; then + info "integrity: IMA kernel support is disabled" + fi + return 0 +fi + +# get the IMA keyring id + +if line=$(keyctl describe %keyring:.ima); then + _ima_id=${line%%:*} +else + _ima_id=$(keyctl search @u keyring _ima) + if [ -z "${_ima_id}" ]; then + _ima_id=$(keyctl newring _ima @u) + fi +fi + +# load the IMA public key(s) +load_x509_keys "${_ima_id}" diff --git a/modules.d/98integrity/ima-policy-load.sh b/modules.d/98integrity/ima-policy-load.sh new file mode 100755 index 0000000..a1fbb4d --- /dev/null +++ b/modules.d/98integrity/ima-policy-load.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# Licensed under the GPLv2 +# +# Copyright (C) 2011 Politecnico di Torino, Italy +# TORSEC group -- http://security.polito.it +# Roberto Sassu <roberto.sassu@polito.it> + +IMASECDIR="${SECURITYFSDIR}/ima" +IMACONFIG="${NEWROOT}/etc/sysconfig/ima" +IMAPOLICY="/etc/sysconfig/ima-policy" + +load_ima_policy() { + # check kernel support for IMA + if [ ! -e "${IMASECDIR}" ]; then + if [ "${RD_DEBUG}" = "yes" ]; then + info "integrity: IMA kernel support is disabled" + fi + return 0 + fi + + # override the default configuration + # shellcheck disable=SC1090 + [ -f "${IMACONFIG}" ] \ + && . "${IMACONFIG}" + + # set the IMA policy path name + IMAPOLICYPATH="${NEWROOT}${IMAPOLICY}" + + # check the existence of the IMA policy file + [ -f "${IMAPOLICYPATH}" ] && { + info "Loading the provided IMA custom policy" + printf '%s' "${IMAPOLICYPATH}" > "${IMASECDIR}"/policy \ + || cat "${IMAPOLICYPATH}" > "${IMASECDIR}"/policy + } + + return 0 +} + +load_ima_policy diff --git a/modules.d/98integrity/module-setup.sh b/modules.d/98integrity/module-setup.sh new file mode 100755 index 0000000..fb2badf --- /dev/null +++ b/modules.d/98integrity/module-setup.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + echo masterkey securityfs + return 0 +} + +# called by dracut +install() { + dracut_install evmctl keyctl + inst_hook pre-pivot 61 "$moddir/evm-enable.sh" + inst_hook pre-pivot 61 "$moddir/ima-keys-load.sh" + inst_hook pre-pivot 62 "$moddir/ima-policy-load.sh" +} diff --git a/modules.d/98pollcdrom/module-setup.sh b/modules.d/98pollcdrom/module-setup.sh new file mode 100755 index 0000000..9684181 --- /dev/null +++ b/modules.d/98pollcdrom/module-setup.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_hook initqueue/settled 99 "$moddir/pollcdrom.sh" +} diff --git a/modules.d/98pollcdrom/pollcdrom.sh b/modules.d/98pollcdrom/pollcdrom.sh new file mode 100755 index 0000000..85305a7 --- /dev/null +++ b/modules.d/98pollcdrom/pollcdrom.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Licensed under the GPLv2 +# +# Copyright 2008-2012, Red Hat, Inc. +# Harald Hoyer <harald@redhat.com> + +if [ ! -e /sys/module/block/parameters/events_dfl_poll_msecs ]; then + # if the kernel does not support autopolling + # then we have to do a + # dirty hack for some cdrom drives, + # which report no medium for quiet + # some time. + for cdrom in /sys/block/sr*; do + [ -e "$cdrom" ] || continue + # skip, if cdrom medium was already found + strstr "$(udevadm info --query=property --path="${cdrom##/sys}")" \ + ID_CDROM_MEDIA && continue + echo change > "$cdrom/uevent" + done +fi diff --git a/modules.d/98selinux/module-setup.sh b/modules.d/98selinux/module-setup.sh new file mode 100755 index 0000000..3574b12 --- /dev/null +++ b/modules.d/98selinux/module-setup.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_hook pre-pivot 50 "$moddir/selinux-loadpolicy.sh" + inst_multiple setenforce chroot +} diff --git a/modules.d/98selinux/selinux-loadpolicy.sh b/modules.d/98selinux/selinux-loadpolicy.sh new file mode 100755 index 0000000..0235b8e --- /dev/null +++ b/modules.d/98selinux/selinux-loadpolicy.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +# FIXME: load selinux policy. this should really be done after we switchroot + +rd_load_policy() { + # If SELinux is disabled exit now + getarg "selinux=0" > /dev/null && return 0 + + SELINUX="enforcing" + # shellcheck disable=SC1090 + [ -e "$NEWROOT/etc/selinux/config" ] && . "$NEWROOT/etc/selinux/config" + + # Check whether SELinux is in permissive mode + permissive=0 + + if getarg "enforcing=0" > /dev/null || [ "$SELINUX" = "permissive" ]; then + permissive=1 + fi + + # Attempt to load SELinux Policy + if [ -x "$NEWROOT/usr/sbin/load_policy" -o -x "$NEWROOT/sbin/load_policy" ]; then + local ret=0 + local out + info "Loading SELinux policy" + mount -o bind /sys "$NEWROOT"/sys + # load_policy does mount /proc and /sys/fs/selinux in + # libselinux,selinux_init_load_policy() + if [ -x "$NEWROOT/sbin/load_policy" ]; then + out=$(LANG=C chroot "$NEWROOT" /sbin/load_policy -i 2>&1) + ret=$? + info "$out" + else + out=$(LANG=C chroot "$NEWROOT" /usr/sbin/load_policy -i 2>&1) + ret=$? + info "$out" + fi + umount "$NEWROOT"/sys/fs/selinux + umount "$NEWROOT"/sys + + if [ "$SELINUX" = "disabled" ]; then + return 0 + fi + + if [ $ret -eq 0 -o $ret -eq 2 ]; then + # If machine requires a relabel, force to permissive mode + [ -e "$NEWROOT"/.autorelabel ] && LANG=C /usr/sbin/setenforce 0 + mount --rbind /dev "$NEWROOT/dev" + LANG=C chroot "$NEWROOT" /sbin/restorecon -R /dev + umount -R "$NEWROOT/dev" + return 0 + fi + + warn "Initial SELinux policy load failed." + if [ $ret -eq 3 -o $permissive -eq 0 ]; then + warn "Machine in enforcing mode." + warn "Not continuing" + emergency_shell -n selinux + exit 1 + fi + return 0 + elif [ $permissive -eq 0 -a "$SELINUX" != "disabled" ]; then + warn "Machine in enforcing mode and cannot execute load_policy." + warn "To disable selinux, add selinux=0 to the kernel command line." + warn "Not continuing" + emergency_shell -n selinux + exit 1 + fi +} + +rd_load_policy diff --git a/modules.d/98syslog/README b/modules.d/98syslog/README new file mode 100644 index 0000000..9eb5ade --- /dev/null +++ b/modules.d/98syslog/README @@ -0,0 +1,24 @@ +Syslog support for dracut + +This module provides syslog functionality in the initrd. +This is especially interesting when complex configuration being +used to provide access to the device the rootfs resides on. + +When this module is installed into the ramfs it is triggered by +the udev event from the nic being setup (online). + +Then if syslog is configured it is started and will forward all +kernel messages to the given syslog server. + +The syslog implementation is detected automatically by finding the +appropriate binary with the following order: +rsyslogd +syslogd +syslog-ng +Then if detected the syslog.conf is generated and syslog is started. + +Bootparameters: +syslogserver=ip Where to syslog to +sysloglevel=level What level has to be logged +syslogtype=rsyslog|syslog|syslogng + Don't auto detect syslog but set it diff --git a/modules.d/98syslog/module-setup.sh b/modules.d/98syslog/module-setup.sh new file mode 100755 index 0000000..2909a56 --- /dev/null +++ b/modules.d/98syslog/module-setup.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# called by dracut +check() { + # do not add this module by default + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + local _installs + if find_binary rsyslogd > /dev/null; then + _installs="rsyslogd" + inst_libdir_file rsyslog/lmnet.so rsyslog/imklog.so rsyslog/imuxsock.so rsyslog/imjournal.so + elif find_binary syslogd > /dev/null; then + _installs="syslogd" + elif find_binary syslog-ng > /dev/null; then + _installs="syslog-ng" + else + derror "Could not find any syslog binary although the syslogmodule" \ + "is selected to be installed. Please check." + fi + if [ -n "$_installs" ]; then + inst_multiple cat $_installs + inst_hook cmdline 90 "$moddir/parse-syslog-opts.sh" + inst_hook cleanup 99 "$moddir/syslog-cleanup.sh" + inst_hook initqueue/online 70 "$moddir/rsyslogd-start.sh" + inst_simple "$moddir/rsyslogd-stop.sh" /sbin/rsyslogd-stop + mkdir -m 0755 -p "${initdir}"/etc/templates + inst_simple "${moddir}/rsyslog.conf" /etc/templates/rsyslog.conf + fi + dracut_need_initqueue +} diff --git a/modules.d/98syslog/parse-syslog-opts.sh b/modules.d/98syslog/parse-syslog-opts.sh new file mode 100755 index 0000000..ccefd9d --- /dev/null +++ b/modules.d/98syslog/parse-syslog-opts.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Parses the syslog commandline options +# +#Bootparameters: +#syslogserver=ip Where to syslog to +#sysloglevel=level What level has to be logged +#syslogtype=rsyslog|syslog|syslogng +# Don't auto detect syslog but set it +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +detect_syslog() { + syslogtype="" + if [ -e /sbin/rsyslogd ]; then + syslogtype="rsyslogd" + elif [ -e /sbin/syslogd ]; then + syslogtype="syslogd" + elif [ -e /sbin/syslog-ng ]; then + syslogtype="syslog-ng" + else + warn "Could not find any syslog binary although the syslogmodule is selected to be installed. Please check." + fi + echo "$syslogtype" + [ -n "$syslogtype" ] +} + +syslogserver=$(getarg syslog.server -d syslog) +syslogfilters=$(getargs syslog.filter -d filter) +syslogtype=$(getarg syslog.type -d syslogtype) + +[ -n "$syslogserver" ] && echo "$syslogserver" > /tmp/syslog.server +[ -n "$syslogfilters" ] && echo "$syslogfilters" > /tmp/syslog.filters +if [ -n "$syslogtype" ]; then + echo "$syslogtype" > /tmp/syslog.type +else + syslogtype=$(detect_syslog) + echo "$syslogtype" > /tmp/syslog.type +fi diff --git a/modules.d/98syslog/rsyslog.conf b/modules.d/98syslog/rsyslog.conf new file mode 100644 index 0000000..218072b --- /dev/null +++ b/modules.d/98syslog/rsyslog.conf @@ -0,0 +1,31 @@ +#rsyslog v3 config file + +# if you experience problems, check +# http://www.rsyslog.com/troubleshoot for assistance + +#### MODULES #### + +$ModLoad imuxsock.so # provides support for local system logging (e.g. via logger command) +$ModLoad imklog.so # provides kernel logging support (previously done by rklogd) +#$ModLoad immark.so # provides --MARK-- message capability + +# Provides UDP syslog reception +#$ModLoad imudp.so +#$UDPServerRun 514 + +# Provides TCP syslog reception +#$ModLoad imtcp.so +#$InputTCPServerRun 514 + + +#### GLOBAL DIRECTIVES #### + +# Use default timestamp format +$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat + +# File syncing capability is disabled by default. This feature is usually not required, +# not useful and an extreme performance hit +#$ActionFileEnableSync on + + +#### RULES #### diff --git a/modules.d/98syslog/rsyslogd-start.sh b/modules.d/98syslog/rsyslogd-start.sh new file mode 100755 index 0000000..d404e51 --- /dev/null +++ b/modules.d/98syslog/rsyslogd-start.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +# Triggered by initqueue/online and starts rsyslogd with bootparameters + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +# prevent starting again if already running +if [ -f /var/run/syslogd.pid ]; then + read -r pid < /var/run/syslogd.pid + kill -0 "$pid" && exit 0 +fi + +rsyslog_config() { + local server="$1" + shift + local syslog_template="$1" + shift + local filters="$*" + local filter= + + cat "$syslog_template" + + ( + # disable shell expansion / globbing + # since filters contain such characters + set -f + for filter in $filters; do + echo "${filter} @${server}" + done + ) + #echo "*.* /tmp/syslog" +} + +[ -f /tmp/syslog.type ] && read -r type < /tmp/syslog.type +[ -f /tmp/syslog.server ] && read -r server < /tmp/syslog.server +[ -f /tmp/syslog.filters ] && read -r filters < /tmp/syslog.filters +[ -z "$filters" ] && filters="kern.*" +[ -f /tmp/syslog.conf ] && read -r conf < /tmp/syslog.conf +[ -z "$conf" ] && conf="/etc/rsyslog.conf" && echo "$conf" > /tmp/syslog.conf + +if [ "$type" = "rsyslogd" ]; then + template=/etc/templates/rsyslog.conf + if [ -n "$server" ]; then + rsyslog_config "$server" "$template" "$filters" > $conf + rsyslogd -c3 + fi +fi diff --git a/modules.d/98syslog/rsyslogd-stop.sh b/modules.d/98syslog/rsyslogd-stop.sh new file mode 100755 index 0000000..3fc2a5f --- /dev/null +++ b/modules.d/98syslog/rsyslogd-stop.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +# Kills rsyslogd + +if [ -f /var/run/syslogd.pid ]; then + read -r pid < /var/run/syslogd.pid + kill "$pid" + kill -0 "$pid" && kill -9 "$pid" +else + warn "rsyslogd-stop: Could not find a pid for rsyslogd. Won't kill it." +fi diff --git a/modules.d/98syslog/syslog-cleanup.sh b/modules.d/98syslog/syslog-cleanup.sh new file mode 100755 index 0000000..2d08d8a --- /dev/null +++ b/modules.d/98syslog/syslog-cleanup.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# Just cleans up a previously started syslogd + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +if [ -f /tmp/syslog.server ]; then + read -r syslogtype < /tmp/syslog.type + if command -v "${syslogtype}-stop" > /dev/null; then + "${syslogtype}"-stop + else + warn "syslog-cleanup: Could not find script to stop syslog of type \"$syslogtype\". Syslog will not be stopped." + fi +fi diff --git a/modules.d/98usrmount/module-setup.sh b/modules.d/98usrmount/module-setup.sh new file mode 100755 index 0000000..bc88deb --- /dev/null +++ b/modules.d/98usrmount/module-setup.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# called by dracut +check() { + [[ $mount_needs ]] && return 1 + return 0 +} + +# called by dracut +depends() { + echo 'fs-lib' +} + +# called by dracut +install() { + if ! dracut_module_included "systemd"; then + inst_hook pre-pivot 50 "$moddir/mount-usr.sh" + fi + : +} diff --git a/modules.d/98usrmount/mount-usr.sh b/modules.d/98usrmount/mount-usr.sh new file mode 100755 index 0000000..0a3eb32 --- /dev/null +++ b/modules.d/98usrmount/mount-usr.sh @@ -0,0 +1,110 @@ +#!/bin/sh + +type info > /dev/null 2>&1 || . /lib/dracut-lib.sh +type fsck_single > /dev/null 2>&1 || . /lib/fs-lib.sh + +filtersubvol() { + local _oldifs + _oldifs="$IFS" + local IFS="," + # shellcheck disable=SC2086 + set -- $1 + IFS="$_oldifs" + while [ $# -gt 0 ]; do + case $1 in + 'subvol='*) : ;; + *) printf '%s' "${1}," ;; + esac + shift + done +} + +fsck_usr() { + local _dev="$1" + local _fs="$2" + local _fsopts="$3" + local _fsckoptions + + if [ -f "$NEWROOT"/fsckoptions ]; then + read -r _fsckoptions < "$NEWROOT"/fsckoptions + fi + + if [ -f "$NEWROOT"/forcefsck ] || getargbool 0 forcefsck; then + _fsckoptions="-f $_fsckoptions" + elif [ -f "$NEWROOT"/.autofsck ]; then + # shellcheck disable=SC1090 + [ -f "$NEWROOT"/etc/sysconfig/autofsck ] && . "$NEWROOT"/etc/sysconfig/autofsck + if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then + AUTOFSCK_OPT="$AUTOFSCK_OPT -f" + fi + if [ -n "$AUTOFSCK_SINGLEUSER" ]; then + warn "*** Warning -- the system did not shut down cleanly. " + warn "*** Dropping you to a shell; the system will continue" + warn "*** when you leave the shell." + emergency_shell + fi + _fsckoptions="$AUTOFSCK_OPT $_fsckoptions" + fi + + fsck_single "$_dev" "$_fs" "$_fsopts" "$_fsckoptions" +} + +mount_usr() { + local _dev _mp _fs _opts _ _usr_found _ _freq _passno + # check, if we have to mount the /usr filesystem + while read -r _dev _mp _fs _opts _freq _passno || [ -n "$_dev" ]; do + [ "${_dev%%#*}" != "$_dev" ] && continue + if [ "$_mp" = "/usr" ]; then + case "$_dev" in + LABEL=* | UUID=* | PARTUUID=* | PARTLABEL=*) + _dev="$(label_uuid_to_dev "$_dev")" + ;; + *) ;; + esac + + if strstr "$_opts" "subvol=" \ + && [ "${root#block:}" -ef "$_dev" ] \ + && [ -n "$rflags" ]; then + # for btrfs subvolumes we have to mount /usr with the same rflags + rflags=$(filtersubvol "$rflags") + rflags=${rflags%%,} + _opts="${_opts:+${_opts},}${rflags}" + elif getargbool 0 ro; then + # if "ro" is specified, we want /usr to be mounted read-only + _opts="${_opts:+${_opts},}ro" + elif getargbool 0 rw; then + # if "rw" is specified, we want /usr to be mounted read-write + _opts="${_opts:+${_opts},}rw" + fi + echo "$_dev ${NEWROOT}${_mp} $_fs ${_opts} $_freq $_passno" + _usr_found="1" + break + fi + done < "$NEWROOT/etc/fstab" >> /etc/fstab + + if [ "$_usr_found" != "" ]; then + # we have to mount /usr + _fsck_ret=0 + if ! getargbool 0 rd.skipfsck; then + if [ "0" != "${_passno:-0}" ]; then + fsck_usr "$_dev" "$_fs" "$_opts" + _fsck_ret=$? + [ $_fsck_ret -ne 255 ] && echo $_fsck_ret > /run/initramfs/usr-fsck + fi + fi + + info "Mounting /usr with -o $_opts" + mount "$NEWROOT/usr" 2>&1 | vinfo + + if ! ismounted "$NEWROOT/usr"; then + warn "Mounting /usr to $NEWROOT/usr failed" + warn "*** Dropping you to a shell; the system will continue" + warn "*** when you leave the shell." + emergency_shell + fi + fi +} + +if [ -f "$NEWROOT/etc/fstab" ]; then + mount_usr +fi diff --git a/modules.d/99base/dracut-dev-lib.sh b/modules.d/99base/dracut-dev-lib.sh new file mode 100755 index 0000000..5779508 --- /dev/null +++ b/modules.d/99base/dracut-dev-lib.sh @@ -0,0 +1,139 @@ +#!/bin/sh + +# replaces all occurrences of 'search' in 'str' with 'replacement' +# +# str_replace str search replacement +# +# example: +# str_replace ' one two three ' ' ' '_' +str_replace() { + local in="$1" + local s="$2" + local r="$3" + local out='' + + while [ "${in##*"$s"*}" != "$in" ]; do + chop="${in%%"$s"*}" + out="${out}${chop}$r" + in="${in#*"$s"}" + done + printf -- '%s' "${out}${in}" +} + +# get a systemd-compatible unit name from a path +# (mimics unit_name_from_path_instance()) +dev_unit_name() { + local dev="$1" + + if command -v systemd-escape > /dev/null; then + systemd-escape -p -- "$dev" + return $? + fi + + if [ "$dev" = "/" -o -z "$dev" ]; then + printf -- "-" + return 0 + fi + + dev="${1%%/}" + dev="${dev##/}" + # shellcheck disable=SC1003 + dev="$(str_replace "$dev" '\' '\x5c')" + dev="$(str_replace "$dev" '-' '\x2d')" + if [ "${dev##.}" != "$dev" ]; then + dev="\x2e${dev##.}" + fi + dev="$(str_replace "$dev" '/' '-')" + + printf -- "%s" "$dev" +} + +# set_systemd_timeout_for_dev [-n] <dev> [<timeout>] +# Set 'rd.timeout' as the systemd timeout for <dev> +set_systemd_timeout_for_dev() { + local _name + local _needreload + local _noreload + local _timeout + + [ -z "$DRACUT_SYSTEMD" ] && return 0 + + if [ "$1" = "-n" ]; then + _noreload=1 + shift + fi + + if [ -n "$2" ]; then + _timeout="$2" + else + _timeout=$(getarg rd.timeout) + fi + + _timeout=${_timeout:-0} + + _name=$(dev_unit_name "$1") + if ! [ -L "${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device" ]; then + [ -d "${PREFIX}"/etc/systemd/system/initrd.target.wants ] || mkdir -p "${PREFIX}"/etc/systemd/system/initrd.target.wants + ln -s ../"${_name}".device "${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device" + type mark_hostonly > /dev/null 2>&1 && mark_hostonly /etc/systemd/system/initrd.target.wants/"${_name}".device + _needreload=1 + fi + + if ! [ -f "${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf" ]; then + mkdir -p "${PREFIX}/etc/systemd/system/${_name}.device.d" + { + echo "[Unit]" + echo "JobTimeoutSec=$_timeout" + echo "JobRunningTimeoutSec=$_timeout" + } > "${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf" + type mark_hostonly > /dev/null 2>&1 && mark_hostonly /etc/systemd/system/"${_name}".device.d/timeout.conf + _needreload=1 + fi + + if [ -z "$PREFIX" ] && [ "$_needreload" = 1 ] && [ -z "$_noreload" ]; then + /sbin/initqueue --onetime --unique --name daemon-reload systemctl daemon-reload + fi +} + +# wait_for_dev <dev> [<timeout>] +# +# Installs a initqueue-finished script, +# which will cause the main loop only to exit, +# if the device <dev> is recognized by the system. +wait_for_dev() { + local _name + local _noreload + + if [ "$1" = "-n" ]; then + _noreload=-n + shift + fi + + _name="$(str_replace "$1" '/' '\x2f')" + + type mark_hostonly > /dev/null 2>&1 && mark_hostonly "$hookdir/initqueue/finished/devexists-${_name}.sh" + + [ -e "${PREFIX}$hookdir/initqueue/finished/devexists-${_name}.sh" ] && return 0 + + printf '[ -e "%s" ]\n' "$1" \ + >> "${PREFIX}$hookdir/initqueue/finished/devexists-${_name}.sh" + { + printf '[ -e "%s" ] || ' "$1" + printf 'warn "\"%s\" does not exist"\n' "$1" + } >> "${PREFIX}$hookdir/emergency/80-${_name}.sh" + + set_systemd_timeout_for_dev $_noreload "$@" +} + +cancel_wait_for_dev() { + local _name + _name="$(str_replace "$1" '/' '\x2f')" + rm -f -- "$hookdir/initqueue/finished/devexists-${_name}.sh" + rm -f -- "$hookdir/emergency/80-${_name}.sh" + if [ -n "$DRACUT_SYSTEMD" ]; then + _name=$(dev_unit_name "$1") + rm -f -- "${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device" + rm -f -- "${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf" + /sbin/initqueue --onetime --unique --name daemon-reload systemctl daemon-reload + fi +} diff --git a/modules.d/99base/dracut-lib.sh b/modules.d/99base/dracut-lib.sh new file mode 100755 index 0000000..39609d8 --- /dev/null +++ b/modules.d/99base/dracut-lib.sh @@ -0,0 +1,1178 @@ +#!/bin/sh + +type wait_for_dev > /dev/null 2>&1 || . /lib/dracut-dev-lib.sh + +export DRACUT_SYSTEMD +export NEWROOT +if [ -n "$NEWROOT" ]; then + [ -d "$NEWROOT" ] || mkdir -p -m 0755 "$NEWROOT" +fi + +# shellcheck disable=SC2153 +if [ -z "$PREFIX" ]; then + if ! [ -d /run/initramfs ]; then + mkdir -p -m 0755 /run/initramfs/log + ln -sfn /run/initramfs/log /var/log + fi + + [ -d /run/lock ] || mkdir -p -m 0755 /run/lock + [ -d /run/log ] || mkdir -p -m 0755 /run/log +fi + +debug_off() { + set +x +} + +debug_on() { + [ "$RD_DEBUG" = "yes" ] && set -x +} + +# returns OK if $1 contains literal string $2 (and isn't empty) +strstr() { + [ "${1##*"$2"*}" != "$1" ] +} + +# returns OK if $1 matches (completely) glob pattern $2 +# An empty $1 will not be considered matched, even if $2 is * which technically +# matches; as it would match anything, it's not an interesting case. +strglob() { + # shellcheck disable=SC2295 + [ -n "$1" -a -z "${1##$2}" ] +} + +# returns OK if $1 contains (anywhere) a match of glob pattern $2 +# An empty $1 will not be considered matched, even if $2 is * which technically +# matches; as it would match anything, it's not an interesting case. +strglobin() { + # shellcheck disable=SC2295 + [ -n "$1" -a -z "${1##*$2*}" ] +} + +# returns OK if $1 contains literal string $2 at the beginning, and isn't empty +str_starts() { + [ "${1#"$2"*}" != "$1" ] +} + +# returns OK if $1 contains literal string $2 at the end, and isn't empty +str_ends() { + [ "${1%*"$2"}" != "$1" ] +} + +trim() { + local var="$*" + var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters + var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters + printf "%s" "$var" +} + +if [ -z "$DRACUT_SYSTEMD" ]; then + + warn() { + check_quiet + echo "<28>dracut Warning: $*" > /dev/kmsg + echo "dracut Warning: $*" >&2 + } + + info() { + check_quiet + echo "<30>dracut: $*" > /dev/kmsg + if [ "$DRACUT_QUIET" != "yes" ]; then + echo "dracut: $*" >&2 + fi + } + +else + + warn() { + echo "Warning: $*" >&2 + } + + info() { + echo "$*" + } + +fi + +vwarn() { + while read -r line || [ -n "$line" ]; do + warn "$line" + done +} + +vinfo() { + while read -r line || [ -n "$line" ]; do + info "$line" + done +} + +killall_proc_mountpoint() { + local _pid + local _killed=0 + for _pid in /proc/*; do + _pid=${_pid##/proc/} + case $_pid in + *[!0-9]*) continue ;; + esac + [ -e "/proc/$_pid/exe" ] || continue + [ -e "/proc/$_pid/root" ] || continue + if strstr "$(ls -l -- "/proc/$_pid" "/proc/$_pid/fd" 2> /dev/null)" "$1"; then + kill -9 "$_pid" + _killed=1 + fi + done + return $_killed +} + +getcmdline() { + local _line + local _i + local CMDLINE_ETC_D + local CMDLINE_ETC + local CMDLINE_PROC + unset _line + + if [ -e /etc/cmdline ]; then + while read -r _line || [ -n "$_line" ]; do + CMDLINE_ETC="$CMDLINE_ETC $_line" + done < /etc/cmdline + fi + for _i in /etc/cmdline.d/*.conf; do + [ -e "$_i" ] || continue + while read -r _line || [ -n "$_line" ]; do + CMDLINE_ETC_D="$CMDLINE_ETC_D $_line" + done < "$_i" + done + if [ -e /proc/cmdline ]; then + while read -r _line || [ -n "$_line" ]; do + CMDLINE_PROC="$CMDLINE_PROC $_line" + done < /proc/cmdline + fi + CMDLINE="$CMDLINE_ETC_D $CMDLINE_ETC $CMDLINE_PROC" + printf "%s" "$CMDLINE" +} + +getarg() { + debug_off + local _deprecated _newoption + CMDLINE=$(getcmdline) + export CMDLINE + while [ $# -gt 0 ]; do + case $1 in + -d) + _deprecated=1 + shift + ;; + -y) + if dracut-getarg "$2" > /dev/null; then + if [ "$_deprecated" = "1" ]; then + if [ -n "$_newoption" ]; then + warn "Kernel command line option '$2' is deprecated, use '$_newoption' instead." + else + warn "Option '$2' is deprecated." + fi + fi + echo 1 + debug_on + return 0 + fi + _deprecated=0 + shift 2 + ;; + -n) + if dracut-getarg "$2" > /dev/null; then + echo 0 + if [ "$_deprecated" = "1" ]; then + if [ -n "$_newoption" ]; then + warn "Kernel command line option '$2' is deprecated, use '$_newoption=0' instead." + else + warn "Option '$2' is deprecated." + fi + fi + debug_on + return 1 + fi + _deprecated=0 + shift 2 + ;; + *) + if [ -z "$_newoption" ]; then + _newoption="$1" + fi + if dracut-getarg "$1"; then + if [ "$_deprecated" = "1" ]; then + if [ -n "$_newoption" ]; then + warn "Kernel command line option '$1' is deprecated, use '$_newoption' instead." + else + warn "Option '$1' is deprecated." + fi + fi + debug_on + return 0 + fi + _deprecated=0 + shift + ;; + esac + done + debug_on + return 1 +} + +# getargbool <defaultval> <args...> +# False if "getarg <args...>" returns "0", "no", or "off". +# True if getarg returns any other non-empty string. +# If not found, assumes <defaultval> - usually 0 for false, 1 for true. +# example: getargbool 0 rd.info +# true: rd.info, rd.info=1, rd.info=xxx +# false: rd.info=0, rd.info=off, rd.info not present (default val is 0) +getargbool() { + local _b + unset _b + local _default + _default="$1" + shift + _b=$(getarg "$@") || _b=${_b:-"$_default"} + if [ -n "$_b" ]; then + [ "$_b" = "0" ] && return 1 + [ "$_b" = "no" ] && return 1 + [ "$_b" = "off" ] && return 1 + fi + return 0 +} + +isdigit() { + case "$1" in + *[!0-9]* | "") return 1 ;; + esac + + return 0 +} + +# getargnum <defaultval> <minval> <maxval> <arg> +# Will echo the arg if it's in range [minval - maxval]. +# If it's not set or it's not valid, will set it <defaultval>. +# Note all values are required to be >= 0 here. +# <defaultval> should be with [minval -maxval]. +getargnum() { + local _b + unset _b + local _default _min _max + _default="$1" + shift + _min="$1" + shift + _max="$1" + shift + _b=$(getarg "$1") || _b=${_b:-"$_default"} + if [ -n "$_b" ]; then + isdigit "$_b" && _b=$((_b)) \ + && [ $_b -ge "$_min" ] && [ $_b -le "$_max" ] && echo $_b && return + fi + echo "$_default" +} + +getargs() { + debug_off + CMDLINE=$(getcmdline) + export CMDLINE + local _val _i _gfound _deprecated + unset _val + unset _gfound + _newoption="$1" + for _i in "$@"; do + if [ "$_i" = "-d" ]; then + _deprecated=1 + continue + fi + + if _val="$(dracut-getargs "$_i")"; then + if [ "$_deprecated" = "1" ]; then + if [ -n "$_newoption" ]; then + warn "Option '$_i' is deprecated, use '$_newoption' instead." + else + warn "Option $_i is deprecated!" + fi + fi + if [ -n "$_val" ]; then + printf '%s\n' "$_val" + fi + _gfound=1 + fi + _deprecated=0 + done + if [ -n "$_gfound" ]; then + debug_on + return 0 + fi + debug_on + return 1 +} + +# Prints value of given option. If option is a flag and it's present, +# it just returns 0. Otherwise 1 is returned. +# $1 = options separated by commas +# $2 = option we are interested in +# +# Example: +# $1 = cipher=aes-cbc-essiv:sha256,hash=sha256,verify +# $2 = hash +# Output: +# sha256 +getoptcomma() { + local line=",$1," + local opt="$2" + local tmp + + case "${line}" in + *,${opt}=*,*) + tmp="${line#*,"${opt}"=}" + echo "${tmp%%,*}" + return 0 + ;; + *,${opt},*) return 0 ;; + esac + return 1 +} + +# Splits given string 'str' with separator 'sep' into variables 'var1', 'var2', +# 'varN'. If number of fields is less than number of variables, remaining are +# not set. If number of fields is greater than number of variables, the last +# variable takes remaining fields. In short - it acts similarly to 'read'. +# +# splitsep sep str var1 var2 varN +# +# example: +# splitsep ':' 'foo:bar:baz' v1 v2 +# in result: +# v1='foo', v2='bar:baz' +# +# TODO: ':' inside fields. +splitsep() { + debug_off + local sep="$1" + local str="$2" + shift 2 + local tmp + + while [ -n "$str" -a "$#" -gt 1 ]; do + tmp="${str%%"$sep"*}" + eval "$1='${tmp}'" + str="${str#"$tmp"}" + str="${str#"$sep"}" + shift + done + [ -n "$str" -a -n "$1" ] && eval "$1='$str'" + debug_on + return 0 +} + +setdebug() { + [ -f /usr/lib/initrd-release ] || return + if [ -z "$RD_DEBUG" ]; then + if [ -e /proc/cmdline ]; then + RD_DEBUG=no + if getargbool 0 rd.debug -d -y rdinitdebug -d -y rdnetdebug; then + RD_DEBUG=yes + [ -n "$BASH" ] \ + && export PS4='${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]-}): ' + fi + fi + export RD_DEBUG + fi + debug_on +} + +setdebug + +source_all() { + local f + local _dir + _dir=$1 + shift + [ "$_dir" ] && [ -d "/$_dir" ] || return + for f in "/$_dir"/*.sh; do + if [ -e "$f" ]; then + # shellcheck disable=SC1090 + # shellcheck disable=SC2240 + . "$f" "$@" + fi + done +} + +hookdir=/lib/dracut/hooks +export hookdir + +source_hook() { + local _dir + _dir=$1 + shift + source_all "/lib/dracut/hooks/$_dir" "$@" +} + +check_finished() { + local f rc=0 + for f in "$hookdir"/initqueue/finished/*.sh; do + [ "$f" = "$hookdir/initqueue/finished/*.sh" ] && return 0 + # shellcheck disable=SC1090 + if [ -e "$f" ] && (. "$f"); then + rm -f "$f" + else + rc=1 + fi + done + return $rc +} + +source_conf() { + local f + [ "$1" ] && [ -d "/$1" ] || return + # shellcheck disable=SC1090 + for f in "/$1"/*.conf; do [ -e "$f" ] && . "$f"; done +} + +die() { + { + echo "<24>dracut: FATAL: $*" + echo "<24>dracut: Refusing to continue" + } > /dev/kmsg + + { + echo "warn dracut: FATAL: \"$*\"" + echo "warn dracut: Refusing to continue" + } >> $hookdir/emergency/01-die.sh + [ -d /run/initramfs ] || mkdir -p -- /run/initramfs + + : > /run/initramfs/.die + + if getargbool 0 "rd.shell"; then + emergency_shell + else + source_hook "shutdown-emergency" + fi + + if [ -n "$DRACUT_SYSTEMD" ]; then + systemctl --no-block --force halt + fi + + exit 1 +} + +check_quiet() { + if [ -z "$DRACUT_QUIET" ]; then + DRACUT_QUIET="yes" + getargbool 0 rd.info -d -y rdinfo && DRACUT_QUIET="no" + getargbool 0 rd.debug -d -y rdinitdebug && DRACUT_QUIET="no" + getarg quiet || DRACUT_QUIET="yes" + a=$(getarg loglevel=) + [ -n "$a" ] && [ "$a" -ge 28 ] && DRACUT_QUIET="yes" + export DRACUT_QUIET + fi +} + +check_occurances() { + # Count the number of times the character $ch occurs in $str + # Return 0 if the count matches the expected number, 1 otherwise + local str="$1" + local ch="$2" + local expected="$3" + local count=0 + + while [ "${str#*"$ch"}" != "${str}" ]; do + str="${str#*"$ch"}" + count=$((count + 1)) + done + + [ $count -eq "$expected" ] +} + +incol2() { + debug_off + local check + local file="$1" + local str="$2" + + [ -z "$file" ] && return 1 + [ -z "$str" ] && return 1 + + while read -r _ check _ || [ -n "$check" ]; do + if [ "$check" = "$str" ]; then + debug_on + return 0 + fi + done < "$file" + debug_on + return 1 +} + +udevsettle() { + # shellcheck disable=SC2086 + udevadm settle --exit-if-exists=$hookdir/initqueue/work $settle_exit_if_exists +} + +udevproperty() { + for i in "$@"; do + udevadm control --property="$i" + done +} + +find_mount() { + local dev wanted_dev + wanted_dev="$(readlink -e -q "$1")" + while read -r dev _ || [ -n "$dev" ]; do + [ "$dev" = "$wanted_dev" ] && echo "$dev" && return 0 + done < /proc/mounts + return 1 +} + +# usage: ismounted <mountpoint> +# usage: ismounted /dev/<device> +if command -v findmnt > /dev/null; then + ismounted() { + findmnt "$1" > /dev/null 2>&1 + } +else + ismounted() { + if [ -b "$1" ]; then + find_mount "$1" > /dev/null && return 0 + return 1 + fi + + while read -r _ m _ || [ -n "$m" ]; do + [ "$m" = "$1" ] && return 0 + done < /proc/mounts + return 1 + } +fi + +# Create udev rule match for a device with its device name, or the udev property +# ID_FS_UUID or ID_FS_LABEL +# +# example: +# udevmatch LABEL=boot +# prints: +# ENV{ID_FS_LABEL}="boot" +# +# TODO: symlinks +udevmatch() { + case "$1" in + UUID=????????-????-????-????-???????????? | LABEL=* | PARTLABEL=* | PARTUUID=????????-????-????-????-????????????) + printf 'ENV{ID_FS_%s}=="%s"' "${1%%=*}" "${1#*=}" + ;; + UUID=*) + printf 'ENV{ID_FS_UUID}=="%s*"' "${1#*=}" + ;; + PARTUUID=*) + printf 'ENV{ID_FS_PARTUUID}=="%s*"' "${1#*=}" + ;; + /dev/?*) printf -- 'KERNEL=="%s"' "${1#/dev/}" ;; + *) return 255 ;; + esac +} + +label_uuid_to_dev() { + local _dev + _dev="${1#block:}" + case "$_dev" in + LABEL=*) + echo "/dev/disk/by-label/$(echo "${_dev#LABEL=}" | sed 's,/,\\x2f,g;s, ,\\x20,g')" + ;; + PARTLABEL=*) + echo "/dev/disk/by-partlabel/$(echo "${_dev#PARTLABEL=}" | sed 's,/,\\x2f,g;s, ,\\x20,g')" + ;; + UUID=*) + echo "/dev/disk/by-uuid/${_dev#UUID=}" + ;; + PARTUUID=*) + echo "/dev/disk/by-partuuid/${_dev#PARTUUID=}" + ;; + *) + echo "$_dev" + ;; + esac +} + +# Prints unique path for potential file inside specified directory. It consists +# of specified directory, prefix and number at the end which is incremented +# until non-existing file is found. +# +# funiq dir prefix +# +# example: +# # ls /mnt +# cdrom0 cdrom1 +# +# # funiq /mnt cdrom +# /mnt/cdrom2 +funiq() { + local dir="$1" + local prefix="$2" + local i=0 + + [ -d "${dir}" ] || return 1 + + while [ -e "${dir}/${prefix}$i" ]; do + i=$((i + 1)) || return 1 + done + + echo "${dir}/${prefix}$i" +} + +# Creates unique directory and prints its path. It's using funiq to generate +# path. +# +# mkuniqdir subdir new_dir_name +mkuniqdir() { + local dir="$1" + local prefix="$2" + local retdir + local retdir_new + + [ -d "${dir}" ] || mkdir -m 0755 -p "${dir}" || return 1 + + retdir=$(funiq "${dir}" "${prefix}") || return 1 + until mkdir -m 0755 "${retdir}" 2> /dev/null; do + retdir_new=$(funiq "${dir}" "${prefix}") || return 1 + [ "$retdir_new" = "$retdir" ] && return 1 + retdir="$retdir_new" + done + + echo "${retdir}" +} + +# Copy the contents of SRC into DEST, merging the contents of existing +# directories (kinda like rsync, or cpio -p). +# Creates DEST if it doesn't exist. Overwrites files with the same names. +# +# copytree SRC DEST +copytree() { + local src="$1" dest="$2" + [ -d "$src" ] || return 1 + mkdir -p "$dest" || return 1 + dest=$(readlink -e -q "$dest") || return 1 + ( + cd "$src" || exit 1 + cp -af . -t "$dest" + ) +} + +# Evaluates command for UUIDs either given as arguments for this function or all +# listed in /dev/disk/by-uuid. UUIDs doesn't have to be fully specified. If +# beginning is given it is expanded to all matching UUIDs. To pass full UUID to +# your command use '$___' as a place holder. Remember to escape '$'! +# +# foreach_uuid_until [ -p prefix ] command UUIDs +# +# prefix - string to put just before $___ +# command - command to be evaluated +# UUIDs - list of UUIDs separated by space +# +# The function returns after *first successful evaluation* of the given command +# with status 0. If evaluation fails for every UUID function returns with +# status 1. +# +# Example: +# foreach_uuid_until "mount -U \$___ /mnt; echo OK; umount /mnt" \ +# "01234 f512 a235567f-12a3-c123-a1b1-01234567abcb" +foreach_uuid_until() ( + cd /dev/disk/by-uuid || return 1 + + [ "$1" = -p ] && local prefix="$2" && shift 2 + local cmd="$1" + shift + local uuids_list="$*" + local uuid + local full_uuid + local ___ + + [ -n "${cmd}" ] || return 1 + + for uuid in ${uuids_list:-*}; do + for full_uuid in "${uuid}"*; do + [ -e "${full_uuid}" ] || continue + # shellcheck disable=SC2034 + ___="${prefix}${full_uuid}" + eval "${cmd}" && return 0 + done + done + + return 1 +) + +# Get kernel name for given device. Device may be the name too (then the same +# is returned), a symlink (full path), UUID (prefixed with "UUID=") or label +# (prefixed with "LABEL="). If just a beginning of the UUID is specified or +# even an empty, function prints all device names which UUIDs match - every in +# single line. +# +# NOTICE: The name starts with "/dev/". +# +# Example: +# devnames UUID=123 +# May print: +# /dev/dm-1 +# /dev/sdb1 +# /dev/sdf3 +devnames() { + local dev="$1" + local d + local names + + case "$dev" in + UUID=*) + # shellcheck disable=SC2016 + dev="$(foreach_uuid_until '! blkid -U $___' "${dev#UUID=}")" \ + && return 255 + [ -z "$dev" ] && return 255 + ;; + LABEL=*) dev="$(blkid -L "${dev#LABEL=}")" || return 255 ;; + /dev/?*) ;; + *) return 255 ;; + esac + + for d in $dev; do + names="$names +$(readlink -e -q "$d")" || return 255 + done + + echo "${names# +}" +} + +usable_root() { + local _i + + [ -d "$1" ] || return 1 + + for _i in "$1"/usr/lib*/ld-*.so "$1"/lib*/ld-*.so; do + [ -e "$_i" ] && return 0 + done + + for _i in proc sys dev; do + [ -e "$1"/$_i ] || return 1 + done + + return 0 +} + +inst_hook() { + local _hookname _unique _name _job _exe + while [ $# -gt 0 ]; do + case "$1" in + --hook) + _hookname="/$2" + shift + ;; + --unique) + _unique="yes" + ;; + --name) + _name="$2" + shift + ;; + *) + break + ;; + esac + shift + done + + if [ -z "$_unique" ]; then + _job="${_name}$$" + else + _job="${_name:-$1}" + _job=${_job##*/} + fi + + _exe=$1 + shift + + [ -x "$_exe" ] || _exe=$(command -v "$_exe") + + if [ -n "$onetime" ]; then + { + # shellcheck disable=SC2016 + echo '[ -e "$_job" ] && rm -f -- "$_job"' + echo "$_exe $*" + } > "/tmp/$$-${_job}.sh" + else + echo "$_exe $*" > "/tmp/$$-${_job}.sh" + fi + + mv -f "/tmp/$$-${_job}.sh" "$hookdir/${_hookname}/${_job}.sh" +} + +# inst_mount_hook <mountpoint> <prio> <name> <script> +# +# Install a mount hook with priority <prio>, +# which executes <script> as soon as <mountpoint> is mounted. +inst_mount_hook() { + local _prio="$2" _jobname="$3" _script="$4" + local _hookname + _hookname="mount-$(str_replace "$1" '/' '\\x2f')" + [ -d "$hookdir/${_hookname}" ] || mkdir -p "$hookdir/${_hookname}" + inst_hook --hook "$_hookname" --unique --name "${_prio}-${_jobname}" "$_script" +} + +# wait_for_mount <mountpoint> +# +# Installs a initqueue-finished script, +# which will cause the main loop only to exit, +# if <mountpoint> is mounted. +wait_for_mount() { + local _name + _name="$(str_replace "$1" '/' '\\x2f')" + printf '. /lib/dracut-lib.sh\nismounted "%s"\n' "$1" \ + >> "$hookdir/initqueue/finished/ismounted-${_name}.sh" + { + printf 'ismounted "%s" || ' "$1" + printf 'warn "\"%s\" is not mounted"\n' "$1" + } >> "$hookdir/emergency/90-${_name}.sh" +} + +killproc() { + debug_off + local _exe + _exe="$(command -v "$1")" + local _sig="$2" + local _i + [ -x "$_exe" ] || return 1 + for _i in /proc/[0-9]*; do + [ "$_i" = "/proc/1" ] && continue + if [ -e "$_i"/_exe ] && [ "$_i/_exe" -ef "$_exe" ]; then + kill "$_sig" "${_i##*/}" + fi + done + debug_on +} + +need_shutdown() { + : > /run/initramfs/.need_shutdown +} + +wait_for_loginit() { + [ "$RD_DEBUG" = "yes" ] || return + [ -e /run/initramfs/loginit.pipe ] || return + debug_off + echo "DRACUT_LOG_END" + exec 0<> /dev/console 1<> /dev/console 2<> /dev/console + # wait for loginit + i=0 + while [ $i -lt 10 ]; do + if [ ! -e /run/initramfs/loginit.pipe ]; then + j=$(jobs) + [ -z "$j" ] && break + [ -z "${j##*Running*}" ] || break + fi + sleep 0.1 + i=$((i + 1)) + done + + if [ $i -eq 10 ]; then + kill %1 > /dev/null 2>&1 + kill "$(while read -r line || [ -n "$line" ]; do echo "$line"; done < /run/initramfs/loginit.pid)" + fi + + setdebug + rm -f -- /run/initramfs/loginit.pipe /run/initramfs/loginit.pid +} + +# pidof version for root +if ! command -v pidof > /dev/null 2> /dev/null; then + pidof() { + debug_off + local _cmd + local _exe + local _rl + local _ret=1 + local i + _cmd="$1" + if [ -z "$_cmd" ]; then + debug_on + return 1 + fi + _exe=$(command -v "$1") + for i in /proc/*/exe; do + [ -e "$i" ] || continue + if [ -n "$_exe" ]; then + [ "$i" -ef "$_exe" ] || continue + else + _rl=$(readlink -f "$i") + [ "${_rl%/"$_cmd"}" != "$_rl" ] || continue + fi + i=${i%/exe} + echo "${i##/proc/}" + _ret=0 + done + debug_on + return $_ret + } +fi + +_emergency_shell() { + local _name="$1" + if [ -n "$DRACUT_SYSTEMD" ]; then + : > /.console_lock + echo "PS1=\"$_name:\\\${PWD}# \"" > /etc/profile + systemctl start dracut-emergency.service + rm -f -- /etc/profile + rm -f -- /.console_lock + else + debug_off + source_hook "$hook" + echo + /sbin/rdsosreport + echo 'You might want to save "/run/initramfs/rdsosreport.txt" to a USB stick or /boot' + echo 'after mounting them and attach it to a bug report.' + if ! RD_DEBUG='' getargbool 0 rd.debug -d -y rdinitdebug -d -y rdnetdebug; then + echo + echo 'To get more debug information in the report,' + echo 'reboot with "rd.debug" added to the kernel command line.' + fi + echo + echo 'Dropping to debug shell.' + echo + export PS1="$_name:\${PWD}# " + [ -e /.profile ] || : > /.profile + + _ctty="$(RD_DEBUG='' getarg rd.ctty=)" && _ctty="/dev/${_ctty##*/}" + if [ -z "$_ctty" ]; then + _ctty=console + while [ -f /sys/class/tty/$_ctty/active ]; do + read -r _ctty < /sys/class/tty/$_ctty/active + _ctty=${_ctty##* } # last one in the list + done + _ctty=/dev/$_ctty + fi + [ -c "$_ctty" ] || _ctty=/dev/tty1 + case "$(/usr/bin/setsid --help 2>&1)" in *--ctty*) CTTY="--ctty" ;; esac + setsid $CTTY /bin/sh -i -l 0<> $_ctty 1<> $_ctty 2<> $_ctty + fi +} + +emergency_shell() { + local _ctty + set +e + local _rdshell_name="dracut" action="Boot" hook="emergency" + local _emergency_action + + if [ "$1" = "-n" ]; then + _rdshell_name=$2 + shift 2 + elif [ "$1" = "--shutdown" ]; then + _rdshell_name=$2 + action="Shutdown" + hook="shutdown-emergency" + shift 2 + fi + + echo + echo + warn "$*" + echo + + _emergency_action=$(getarg rd.emergency) + [ -z "$_emergency_action" ] \ + && [ -e /run/initramfs/.die ] \ + && _emergency_action=halt + + if getargbool 1 rd.shell -d -y rdshell || getarg rd.break -d rdbreak; then + _emergency_shell "$_rdshell_name" + else + source_hook "$hook" + warn "$action has failed. To debug this issue add \"rd.shell rd.debug\" to the kernel command line." + [ -z "$_emergency_action" ] && _emergency_action=halt + fi + + case "$_emergency_action" in + reboot) + reboot || exit 1 + ;; + poweroff) + poweroff || exit 1 + ;; + halt) + halt || exit 1 + ;; + esac +} + +# Retain the values of these variables but ensure that they are unexported +# This is a POSIX-compliant equivalent of bash's "export -n" +export_n() { + local var + local val + for var in "$@"; do + eval val=\$$var + unset $var + [ -n "$val" ] && eval "$var=\"$val\"" + done +} + +# returns OK if list1 contains all elements of list2, i.e. checks if list2 is a +# sublist of list1. An order and a duplication doesn't matter. +# +# $1 = separator +# $2 = list1 +# $3 = list2 +# $4 = ignore values, separated by $1 +listlist() { + local _sep="$1" + local _list="${_sep}${2}${_sep}" + local _sublist="$3" + [ -n "$4" ] && local _iglist="${_sep}${4}${_sep}" + local IFS="$_sep" + local _v + + [ "$_list" = "$_sublist" ] && return 0 + + for _v in $_sublist; do + if [ -n "$_v" ] && ! ([ -n "$_iglist" ] && strstr "$_iglist" "$_v"); then + strstr "$_list" "$_v" || return 1 + fi + done + + return 0 +} + +# returns OK if both lists contain the same values. An order and a duplication +# doesn't matter. +# +# $1 = separator +# $2 = list1 +# $3 = list2 +# $4 = ignore values, separated by $1 +are_lists_eq() { + listlist "$1" "$2" "$3" "$4" && listlist "$1" "$3" "$2" "$4" +} + +setmemdebug() { + if [ -z "$DEBUG_MEM_LEVEL" ]; then + DEBUG_MEM_LEVEL=$(getargnum 0 0 5 rd.memdebug) + export DEBUG_MEM_LEVEL + fi +} + +setmemdebug + +# parameters: func log_level prefix msg [trace_level:trace]... +make_trace_mem() { + local log_level prefix msg msg_printed + local trace trace_level trace_in_higher_levels insert_trace + + msg=$1 + shift + + prefix='[debug_mem]' + log_level=$DEBUG_MEM_LEVEL + + if [ -z "$log_level" ] || [ "$log_level" -le 0 ]; then + return + fi + + # FIXME? useless echo? + # shellcheck disable=SC2116 + msg=$(echo "$msg") + + msg_printed=0 + while [ $# -gt 0 ]; do + trace=${1%%:*} + trace_level=${trace%%+} + [ "$trace" != "$trace_level" ] && trace_in_higher_levels="yes" + trace=${1##*:} + + if [ -z "$trace_level" ]; then + trace_level=0 + fi + + insert_trace=0 + if [ -n "$trace_in_higher_levels" ]; then + if [ "$log_level" -ge "$trace_level" ]; then + insert_trace=1 + fi + else + if [ "$log_level" -eq "$trace_level" ]; then + insert_trace=1 + fi + fi + + if [ $insert_trace -eq 1 ]; then + if [ $msg_printed -eq 0 ]; then + echo "$prefix $msg" + msg_printed=1 + fi + show_memstats "$trace" + fi + shift + done +} + +# parameters: type +show_memstats() { + case $1 in + shortmem) + while read -r line || [ -n "$line" ]; do + str_starts "$line" "MemFree" \ + || str_starts "$line" "Cached" \ + || str_starts "$line" "Slab" \ + || continue + echo "$line" + done < /proc/meminfo + ;; + mem) + cat /proc/meminfo + ;; + slab) + cat /proc/slabinfo + ;; + iomem) + cat /proc/iomem + ;; + esac +} + +remove_hostonly_files() { + rm -fr /etc/cmdline /etc/cmdline.d/*.conf "$hookdir/initqueue/finished" + if [ -f /lib/dracut/hostonly-files ]; then + while read -r line || [ -n "$line" ]; do + [ -e "$line" ] || [ -h "$line" ] || continue + rm -f "$line" + done < /lib/dracut/hostonly-files + fi +} + +# parameter: kernel_module [filesystem_name] +# returns OK if kernel_module is loaded +# modprobe fails if /lib/modules is not available (--no-kernel use case) +load_fstype() { + local - fs _fs="${2:-$1}" + set +x + while read -r d fs || [ "$d" ]; do + [ "${fs:-$d}" = "$_fs" ] && return 0 + done < /proc/filesystems + modprobe "$1" +} + +# parameter: size of live image +# calls emergency shell if ram size is too small for the image +check_live_ram() { + minmem=$(getarg rd.minmem) + minmem=${minmem:-1024} + imgsize=$1 + memsize=$(($(sed -n 's/MemTotal: *\([[:digit:]]*\).*/\1/p' /proc/meminfo) / 1024)) + + if [ -z "$imgsize" ]; then + warn "Image size could not be determined" + return 0 + fi + + if [ $((memsize - imgsize)) -lt "$minmem" ]; then + sed -i "N;/and attach it to a bug report./s/echo$/echo\n\ + echo \n\ + echo 'Warning!!!'\n\ + echo 'The memory size of your system is too small for this live image.'\n\ + echo 'Expect killed processes due to out of memory conditions.'\n\ + echo \n/" /usr/bin/dracut-emergency + + emergency_shell + fi +} diff --git a/modules.d/99base/init.sh b/modules.d/99base/init.sh new file mode 100755 index 0000000..285059e --- /dev/null +++ b/modules.d/99base/init.sh @@ -0,0 +1,399 @@ +#!/bin/sh +# +# Licensed under the GPLv2 +# +# Copyright 2008-2010, Red Hat, Inc. +# Harald Hoyer <harald@redhat.com> +# Jeremy Katz <katzj@redhat.com> + +export -p > /tmp/export.orig + +NEWROOT="/sysroot" +[ -d $NEWROOT ] || mkdir -p -m 0755 $NEWROOT + +OLDPATH=$PATH +PATH=/usr/sbin:/usr/bin:/sbin:/bin +export PATH + +# mount some important things +if [ ! -d /proc/self ]; then + if ! mount -t proc -o nosuid,noexec,nodev proc /proc > /dev/null; then + echo "Cannot mount proc on /proc! Compile the kernel with CONFIG_PROC_FS!" + exit 1 + fi +fi + +if [ ! -d /sys/kernel ]; then + if ! mount -t sysfs -o nosuid,noexec,nodev sysfs /sys > /dev/null; then + echo "Cannot mount sysfs on /sys! Compile the kernel with CONFIG_SYSFS!" + exit 1 + fi +fi + +RD_DEBUG="" +. /lib/dracut-lib.sh + +setdebug + +if ! ismounted /dev; then + mount -t devtmpfs -o mode=0755,noexec,nosuid,strictatime devtmpfs /dev > /dev/null +fi + +if ! ismounted /dev; then + echo "Cannot mount devtmpfs on /dev! Compile the kernel with CONFIG_DEVTMPFS!" + exit 1 +fi + +# prepare the /dev directory +[ ! -h /dev/fd ] && ln -s /proc/self/fd /dev/fd > /dev/null 2>&1 +[ ! -h /dev/stdin ] && ln -s /proc/self/fd/0 /dev/stdin > /dev/null 2>&1 +[ ! -h /dev/stdout ] && ln -s /proc/self/fd/1 /dev/stdout > /dev/null 2>&1 +[ ! -h /dev/stderr ] && ln -s /proc/self/fd/2 /dev/stderr > /dev/null 2>&1 + +if ! ismounted /dev/pts; then + mkdir -m 0755 -p /dev/pts + mount -t devpts -o gid=5,mode=620,noexec,nosuid devpts /dev/pts > /dev/null +fi + +if ! ismounted /dev/shm; then + mkdir -m 0755 -p /dev/shm + mount -t tmpfs -o mode=1777,noexec,nosuid,nodev,strictatime tmpfs /dev/shm > /dev/null +fi + +if ! ismounted /run; then + mkdir -m 0755 -p /newrun + if ! str_starts "$(readlink -f /bin/sh)" "/run/"; then + mount -t tmpfs -o mode=0755,noexec,nosuid,nodev,strictatime tmpfs /newrun > /dev/null + else + # the initramfs binaries are located in /run, so don't mount it with noexec + mount -t tmpfs -o mode=0755,nosuid,nodev,strictatime tmpfs /newrun > /dev/null + fi + cp -a /run/* /newrun > /dev/null 2>&1 + mount --move /newrun /run + rm -fr -- /newrun +fi + +if command -v kmod > /dev/null 2> /dev/null; then + kmod static-nodes --format=tmpfiles 2> /dev/null \ + | while read -r type file mode _ _ _ majmin || [ -n "$type" ]; do + type=${type%\!} + case $type in + d) + mkdir -m "$mode" -p "$file" + ;; + c) + mknod -m "$mode" "$file" "$type" "${majmin%:*}" "${majmin#*:}" + ;; + esac + done +fi + +trap "emergency_shell Signal caught!" 0 + +export UDEVRULESD=/run/udev/rules.d +[ -d /run/udev ] || mkdir -p -m 0755 /run/udev +[ -d "$UDEVRULESD" ] || mkdir -p -m 0755 "$UDEVRULESD" + +if [ "$RD_DEBUG" = "yes" ]; then + mkfifo /run/initramfs/loginit.pipe + loginit "$DRACUT_QUIET" < /run/initramfs/loginit.pipe > /dev/console 2>&1 & + exec > /run/initramfs/loginit.pipe 2>&1 +else + exec 0<> /dev/console 1<> /dev/console 2<> /dev/console +fi + +[ -f /usr/lib/initrd-release ] && . /usr/lib/initrd-release +[ -n "$VERSION_ID" ] && info "$NAME-$VERSION_ID" + +source_conf /etc/conf.d + +if getarg "rd.cmdline=ask"; then + echo "Enter additional kernel command line parameter (end with ctrl-d or .)" + while read -r -p "> " ${BASH:+-e} line || [ -n "$line" ]; do + [ "$line" = "." ] && break + echo "$line" >> /etc/cmdline.d/99-cmdline-ask.conf + done +fi + +if ! getargbool 1 'rd.hostonly'; then + [ -f /etc/cmdline.d/99-cmdline-ask.conf ] && mv /etc/cmdline.d/99-cmdline-ask.conf /tmp/99-cmdline-ask.conf + remove_hostonly_files + [ -f /tmp/99-cmdline-ask.conf ] && mv /tmp/99-cmdline-ask.conf /etc/cmdline.d/99-cmdline-ask.conf +fi + +# run scriptlets to parse the command line +make_trace_mem "hook cmdline" '1+:mem' '1+:iomem' '3+:slab' +getarg 'rd.break=cmdline' -d 'rdbreak=cmdline' && emergency_shell -n cmdline "Break before cmdline" +source_hook cmdline + +[ -z "$root" ] && die "No or empty root= argument" +[ -z "$rootok" ] && die "Don't know how to handle 'root=$root'" + +export root rflags fstype netroot NEWROOT + +# pre-udev scripts run before udev starts, and are run only once. +make_trace_mem "hook pre-udev" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=pre-udev' -d 'rdbreak=pre-udev' && emergency_shell -n pre-udev "Break before pre-udev" +source_hook pre-udev + +UDEV_LOG=err +getargbool 0 rd.udev.info -d -y rdudevinfo && UDEV_LOG=info +getargbool 0 rd.udev.debug -d -y rdudevdebug && UDEV_LOG=debug + +# start up udev and trigger cold plugs +UDEV_LOG=$UDEV_LOG "$systemdutildir"/systemd-udevd --daemon --resolve-names=never + +UDEV_QUEUE_EMPTY="udevadm settle --timeout=0" + +udevproperty "hookdir=$hookdir" + +make_trace_mem "hook pre-trigger" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=pre-trigger' -d 'rdbreak=pre-trigger' && emergency_shell -n pre-trigger "Break before pre-trigger" +source_hook pre-trigger + +udevadm control --reload > /dev/null 2>&1 || : +# then the rest +udevadm trigger --type=subsystems --action=add > /dev/null 2>&1 +udevadm trigger --type=devices --action=add > /dev/null 2>&1 + +make_trace_mem "hook initqueue" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=initqueue' -d 'rdbreak=initqueue' && emergency_shell -n initqueue "Break before initqueue" + +RDRETRY=$(getarg rd.retry -d 'rd_retry=') +RDRETRY=${RDRETRY:-180} +RDRETRY=$((RDRETRY * 2)) +export RDRETRY +main_loop=0 +export main_loop +while :; do + + check_finished && break + + udevsettle + + check_finished && break + + if [ -f "$hookdir"/initqueue/work ]; then + rm -f -- "$hookdir"/initqueue/work + fi + + for job in "$hookdir"/initqueue/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + check_finished && break 2 + done + + $UDEV_QUEUE_EMPTY > /dev/null 2>&1 || continue + + for job in "$hookdir"/initqueue/settled/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + check_finished && break 2 + done + + $UDEV_QUEUE_EMPTY > /dev/null 2>&1 || continue + + # no more udev jobs and queues empty. + sleep 0.5 + + if [ $main_loop -gt $((2 * RDRETRY / 3)) ]; then + for job in "$hookdir"/initqueue/timeout/*.sh; do + [ -e "$job" ] || break + # shellcheck disable=SC2097 disable=SC1090 disable=SC2098 + job=$job . "$job" + udevadm settle --timeout=0 > /dev/null 2>&1 || main_loop=0 + [ -f "$hookdir"/initqueue/work ] && main_loop=0 + done + fi + + main_loop=$((main_loop + 1)) + [ $main_loop -gt $RDRETRY ] \ + && { + flock -s 9 + emergency_shell "Could not boot." + } 9> /.console_lock +done +unset job +unset queuetriggered +unset main_loop +unset RDRETRY + +# pre-mount happens before we try to mount the root filesystem, +# and happens once. +make_trace_mem "hook pre-mount" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=pre-mount' -d 'rdbreak=pre-mount' && emergency_shell -n pre-mount "Break before pre-mount" +source_hook pre-mount + +getarg 'rd.break=mount' -d 'rdbreak=mount' && emergency_shell -n mount "Break before mount" +# mount scripts actually try to mount the root filesystem, and may +# be sourced any number of times. As soon as one succeeds, no more are sourced. +_i_mount=0 +while :; do + if ismounted "$NEWROOT"; then + usable_root "$NEWROOT" && break + umount "$NEWROOT" + fi + for f in "$hookdir"/mount/*.sh; do + # shellcheck disable=SC1090 + [ -f "$f" ] && . "$f" + if ismounted "$NEWROOT"; then + usable_root "$NEWROOT" && break + warn "$NEWROOT has no proper rootfs layout, ignoring and removing offending mount hook" + umount "$NEWROOT" + rm -f -- "$f" + fi + done + + _i_mount=$((_i_mount + 1)) + [ $_i_mount -gt 20 ] \ + && { + flock -s 9 + emergency_shell "Can't mount root filesystem" + } 9> /.console_lock +done + +{ + printf "Mounted root filesystem " + while read -r dev mp _ || [ -n "$dev" ]; do [ "$mp" = "$NEWROOT" ] && echo "$dev"; done < /proc/mounts +} | vinfo + +# pre pivot scripts are sourced just before we doing cleanup and switch over +# to the new root. +make_trace_mem "hook pre-pivot" '1:shortmem' '2+:mem' '3+:slab' +getarg 'rd.break=pre-pivot' -d 'rdbreak=pre-pivot' && emergency_shell -n pre-pivot "Break before pre-pivot" +source_hook pre-pivot + +make_trace_mem "hook cleanup" '1:shortmem' '2+:mem' '3+:slab' +# pre pivot cleanup scripts are sourced just before we switch over to the new root. +getarg 'rd.break=cleanup' -d 'rdbreak=cleanup' && emergency_shell -n cleanup "Break before cleanup" +source_hook cleanup + +# By the time we get here, the root filesystem should be mounted. +# Try to find init. +for i in "$(getarg real_init=)" "$(getarg init=)" $(getargs rd.distroinit=) /sbin/init; do + [ -n "$i" ] || continue + + __p="${NEWROOT}/${i}" + if [ -h "$__p" ]; then + # relative links need to be left alone, + # while absolute links need to be resolved and prefixed. + __pt=$(readlink "$__p") + [ "${__pt#/}" = "$__pt" ] || __p="${NEWROOT}/$__pt" + fi + if [ -x "$__p" ]; then + INIT="$i" + break + fi +done + +[ "$INIT" ] || { + echo "Cannot find init!" + echo "Please check to make sure you passed a valid root filesystem!" + emergency_shell +} + +udevadm control --exit +udevadm info --cleanup-db + +debug_off # Turn off debugging for this section + +CAPSH=$(command -v capsh) +SWITCH_ROOT=$(command -v switch_root) + +# unexport some vars +export_n root rflags fstype netroot NEWROOT +unset CMDLINE + +# Clean up the environment +for i in $(export -p); do + i=${i#declare -x} + i=${i#export} + strstr "$i" "=" || continue + i=${i%%=*} + [ -z "$i" ] && continue + case $i in + root | PATH | HOME | TERM | PS4 | RD_*) + : + ;; + *) + unset "$i" + ;; + esac +done +. /tmp/export.orig 2> /dev/null || : +rm -f -- /tmp/export.orig + +initargs="" +read -r CLINE < /proc/cmdline +if getarg init= > /dev/null; then + ignoreargs="console BOOT_IMAGE" + # only pass arguments after init= to the init + CLINE=${CLINE#*init=} + # shellcheck disable=SC2086 + set -- $CLINE + shift # clear out the rest of the "init=" arg + for x in "$@"; do + for s in $ignoreargs; do + [ "${x%%=*}" = "$s" ] && continue 2 + done + initargs="$initargs $x" + done + unset CLINE +else + debug_off # Turn off debugging for this section + # shellcheck disable=SC2086 + set -- $CLINE + for x in "$@"; do + case "$x" in + [0-9] | s | S | single | emergency | auto) + initargs="$initargs $x" + ;; + esac + done +fi +debug_on + +if ! [ -d "$NEWROOT"/run ]; then + NEWRUN=/dev/.initramfs + mkdir -m 0755 -p "$NEWRUN" + mount --rbind /run/initramfs "$NEWRUN" +fi + +wait_for_loginit + +# remove helper symlink +[ -h /dev/root ] && rm -f -- /dev/root + +bv=$(getarg rd.break -d rdbreak) && [ -z "$bv" ] \ + && emergency_shell -n switch_root "Break before switch_root" +unset bv +info "Switching root" + +unset PS4 + +PATH=$OLDPATH +export PATH + +if [ -f /etc/capsdrop ]; then + . /etc/capsdrop + info "Calling $INIT with capabilities $CAPS_INIT_DROP dropped." + unset RD_DEBUG + exec "$CAPSH" --drop="$CAPS_INIT_DROP" -- \ + -c "exec \"$SWITCH_ROOT\" \"$NEWROOT\" \"$INIT\" $initargs" \ + || { + warn "Command:" + warn capsh --drop="$CAPS_INIT_DROP" -- -c exec "$SWITCH_ROOT" "$NEWROOT" "$INIT" "$initargs" + warn "failed." + emergency_shell + } +else + unset RD_DEBUG + # shellcheck disable=SC2086 + exec "$SWITCH_ROOT" "$NEWROOT" "$INIT" $initargs || { + warn "Something went very badly wrong in the initramfs. Please " + warn "file a bug against dracut." + emergency_shell + } +fi diff --git a/modules.d/99base/initqueue.sh b/modules.d/99base/initqueue.sh new file mode 100755 index 0000000..2c49079 --- /dev/null +++ b/modules.d/99base/initqueue.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# +# Licensed under the GPLv2+ +# +# Copyright 2008-2010, Red Hat, Inc. +# Harald Hoyer <harald@redhat.com> + +PATH=/usr/sbin:/usr/bin:/sbin:/bin + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +while [ $# -gt 0 ]; do + case "$1" in + --onetime) + onetime="yes" + ;; + --online) + qname="/online" + ;; + --settled) + qname="/settled" + ;; + --finished) + qname="/finished" + ;; + --timeout) + qname="/timeout" + ;; + --unique) + unique="yes" + ;; + --name) + name="$2" + shift + ;; + --env) + env="$2" + shift + ;; + *) + break + ;; + esac + shift +done + +if [ -z "$unique" ]; then + job="${name}$$" +else + job="${name:-$1}" + job=${job##*/} +fi + +exe=$1 +shift + +[ -x "$exe" ] || exe=$(command -v "$exe") +if [ -z "$exe" ]; then + echo "Invalid command" + exit 1 +fi + +{ + # shellcheck disable=SC2016 + [ -n "$onetime" ] && echo '[ -e "$job" ] && rm -f -- "$job"' + [ -n "$env" ] && echo "$env" + echo "$exe" "$@" +} > "/tmp/$$-${job}.sh" + +mv -f "/tmp/$$-${job}.sh" "$hookdir/initqueue${qname}/${job}.sh" +[ -z "$qname" ] && : >> "$hookdir"/initqueue/work +exit 0 diff --git a/modules.d/99base/loginit.sh b/modules.d/99base/loginit.sh new file mode 100755 index 0000000..cdb305b --- /dev/null +++ b/modules.d/99base/loginit.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# turn off debugging +set +x + +QUIET=$1 + +printf "%s" "$$" > /run/initramfs/loginit.pid + +# shellcheck disable=SC2015 +[ -e /dev/kmsg ] && exec 5> /dev/kmsg || exec 5> /dev/null +exec 6> /run/initramfs/init.log + +while read -r line || [ -n "$line" ]; do + if [ "$line" = "DRACUT_LOG_END" ]; then + rm -f -- /run/initramfs/loginit.pipe + exit 0 + fi + echo "<31>dracut: $line" >&5 + # if "quiet" is specified we output to /dev/console + [ -n "$QUIET" ] || echo "dracut: $line" + echo "$line" >&6 +done diff --git a/modules.d/99base/module-setup.sh b/modules.d/99base/module-setup.sh new file mode 100755 index 0000000..54b0deb --- /dev/null +++ b/modules.d/99base/module-setup.sh @@ -0,0 +1,146 @@ +#!/bin/bash + +# called by dracut +check() { + return 0 +} + +# called by dracut +depends() { + echo udev-rules + return 0 +} + +# called by dracut +install() { + inst_multiple mount mknod mkdir sleep chown \ + sed ls flock cp mv dmesg rm ln rmmod mkfifo umount readlink setsid \ + modprobe chmod tr + + inst_multiple -o findmnt less kmod + + inst_binary "${dracutbasedir}/dracut-util" "/usr/bin/dracut-util" + + ln -s dracut-util "${initdir}/usr/bin/dracut-getarg" + ln -s dracut-util "${initdir}/usr/bin/dracut-getargs" + + if [ ! -e "${initdir}/bin/sh" ]; then + inst_multiple bash + (ln -s bash "${initdir}/bin/sh" || :) + fi + + # add common users in /etc/passwd, it will be used by nfs/ssh currently + # use password for hostonly images to facilitate secure sulogin in emergency console + [[ $hostonly ]] && pwshadow='x' + grep '^root:' "$initdir/etc/passwd" > /dev/null 2>&1 || echo "root:$pwshadow:0:0::/root:/bin/sh" >> "$initdir/etc/passwd" + grep '^nobody:' "$dracutsysrootdir"/etc/passwd >> "$initdir/etc/passwd" + + [[ $hostonly ]] && grep '^root:' "$dracutsysrootdir"/etc/shadow >> "$initdir/etc/shadow" + + # install our scripts and hooks + inst_script "$moddir/init.sh" "/init" + inst_script "$moddir/initqueue.sh" "/sbin/initqueue" + inst_script "$moddir/loginit.sh" "/sbin/loginit" + inst_script "$moddir/rdsosreport.sh" "/sbin/rdsosreport" + + [ -e "${initdir}/lib" ] || mkdir -m 0755 -p "${initdir}"/lib + mkdir -m 0755 -p "${initdir}"/lib/dracut + mkdir -m 0755 -p "${initdir}"/lib/dracut/hooks + + mkdir -p "${initdir}"/tmp + + inst_simple "$moddir/dracut-lib.sh" "/lib/dracut-lib.sh" + inst_simple "$moddir/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" + mkdir -p "${initdir}"/var + + if ! dracut_module_included "systemd"; then + inst_multiple switch_root || dfatal "Failed to install switch_root" + inst_hook cmdline 10 "$moddir/parse-root-opts.sh" + fi + + if [[ $realinitpath ]]; then + for i in $realinitpath; do + echo "rd.distroinit=$i" + done > "${initdir}/etc/cmdline.d/distroinit.conf" + fi + + ln -fs /proc/self/mounts "$initdir/etc/mtab" + if [[ $ro_mnt == yes ]]; then + echo ro >> "${initdir}/etc/cmdline.d/base.conf" + fi + + [ -e "${initdir}/usr/lib" ] || mkdir -m 0755 -p "${initdir}"/usr/lib + + local VERSION="" + local PRETTY_NAME="" + # Derive an os-release file from the host, if it exists + if [[ -e $dracutsysrootdir/etc/os-release ]]; then + # shellcheck disable=SC1090 + . "$dracutsysrootdir"/etc/os-release + grep -hE -ve '^VERSION=' -ve '^PRETTY_NAME' "$dracutsysrootdir"/etc/os-release > "${initdir}"/usr/lib/initrd-release + [[ -n ${VERSION} ]] && VERSION+=" " + [[ -n ${PRETTY_NAME} ]] && PRETTY_NAME+=" " + else + # Fall back to synthesizing one, since dracut is presently used + # on non-systemd systems as well. + { + echo "NAME=dracut" + echo "ID=dracut" + echo "VERSION_ID=\"$DRACUT_VERSION\"" + echo 'ANSI_COLOR="0;34"' + } > "${initdir}"/usr/lib/initrd-release + fi + VERSION+="dracut-$DRACUT_VERSION" + PRETTY_NAME+="dracut-$DRACUT_VERSION (Initramfs)" + { + echo "VERSION=\"$VERSION\"" + echo "PRETTY_NAME=\"$PRETTY_NAME\"" + # This addition is relatively new, intended to allow software + # to easily detect the dracut version if need be without + # having it mixed in with the real underlying OS version. + echo "DRACUT_VERSION=\"${DRACUT_VERSION}\"" + } >> "$initdir"/usr/lib/initrd-release + echo "dracut-$DRACUT_VERSION" > "$initdir/lib/dracut/dracut-$DRACUT_VERSION" + ln -sf ../usr/lib/initrd-release "$initdir"/etc/initrd-release + ln -sf initrd-release "$initdir"/usr/lib/os-release + ln -sf initrd-release "$initdir"/etc/os-release + + ## save host_devs which we need bring up + if [[ $hostonly_cmdline == "yes" ]]; then + if [[ -n ${host_devs[*]} ]]; then + dracut_need_initqueue + fi + if [[ -f $initdir/lib/dracut/need-initqueue ]] || ! dracut_module_included "systemd"; then + ( + if dracut_module_included "systemd"; then + export DRACUT_SYSTEMD=1 + fi + export PREFIX="$initdir" + export hookdir=/lib/dracut/hooks + + # shellcheck source=dracut-dev-lib.sh + . "$moddir/dracut-dev-lib.sh" + + for _dev in "${host_devs[@]}"; do + for _dev2 in "${root_devs[@]}"; do + [[ $_dev == "$_dev2" ]] && continue 2 + done + + # We only actually wait for real devs - swap is only needed + # for resume and udev rules generated when parsing resume= + # argument take care of the waiting for us + for _dev2 in "${swap_devs[@]}"; do + [[ $_dev == "$_dev2" ]] && continue 2 + done + + _pdev=$(get_persistent_dev "$_dev") + + case "$_pdev" in + /dev/?*) wait_for_dev "$_pdev" 0 ;; + *) ;; + esac + done + ) + fi + fi +} diff --git a/modules.d/99base/parse-root-opts.sh b/modules.d/99base/parse-root-opts.sh new file mode 100755 index 0000000..9525249 --- /dev/null +++ b/modules.d/99base/parse-root-opts.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# shellcheck disable=SC2034 +root=$(getarg root=) + +rflags="$(getarg rootflags=)" +getargbool 0 ro && rflags="${rflags},ro" +getargbool 0 rw && rflags="${rflags},rw" +rflags="${rflags#,}" + +fstype="$(getarg rootfstype=)" +if [ -z "$fstype" ]; then + fstype="auto" +fi diff --git a/modules.d/99base/rdsosreport.sh b/modules.d/99base/rdsosreport.sh new file mode 100755 index 0000000..dadf30b --- /dev/null +++ b/modules.d/99base/rdsosreport.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +echo 'Generating "/run/initramfs/rdsosreport.txt"' + +[ -d /run/initramfs ] || mkdir -p /run/initramfs + +exec > /run/initramfs/rdsosreport.txt 2>&1 + +PWFILTER='s/\(ftp:\/\/.*\):.*@/\1:*******@/g;s/\(cifs:\/\/.*\):.*@/\1:*******@/g;s/cifspass=[^ ]*/cifspass=*******/g;s/iscsi:.*@/iscsi:******@/g;s/rd.iscsi.password=[^ ]*/rd.iscsi.password=******/g;s/rd.iscsi.in.password=[^ ]*/rd.iscsi.in.password=******/g' +set -x +cat /lib/dracut/dracut-* + +echo "/proc/cmdline" +sed -e "$PWFILTER" /proc/cmdline + +if [ -f /etc/cmdline ]; then + echo "/etc/cmdline" + sed -e "$PWFILTER" /etc/cmdline +fi + +for _i in /etc/cmdline.d/*.conf; do + [ -f "$_i" ] || break + echo "$_i" + sed -e "$PWFILTER" "$_i" +done + +cat /proc/self/mountinfo +cat /proc/mounts + +blkid +blkid -o udev + +ls -l /dev/disk/by* + +for _i in /etc/conf.d/*.conf; do + [ -f "$_i" ] || break + echo "$_i" + sed -e "$PWFILTER" "$_i" +done + +if command -v lvm > /dev/null 2> /dev/null; then + lvm pvdisplay + lvm vgdisplay + lvm lvdisplay +fi + +command -v dmsetup > /dev/null 2> /dev/null && dmsetup ls --tree + +cat /proc/mdstat + +command -v ip > /dev/null 2> /dev/null && ip addr + +if command -v journalctl > /dev/null 2> /dev/null; then + journalctl -ab --no-pager -o short-monotonic | sed -e "$PWFILTER" +else + dmesg | sed -e "$PWFILTER" + if [ -f /run/initramfs/init.log ]; then + echo "/run/initramfs/init.log" + sed -e "$PWFILTER" /run/initramfs/init.log + fi +fi diff --git a/modules.d/99fs-lib/fs-lib.sh b/modules.d/99fs-lib/fs-lib.sh new file mode 100755 index 0000000..c4640fa --- /dev/null +++ b/modules.d/99fs-lib/fs-lib.sh @@ -0,0 +1,262 @@ +#!/bin/sh + +type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh + +fsck_ask_reboot() { + info "note - fsck suggests reboot, if you" + info "leave shell, booting will continue normally" + emergency_shell -n "(reboot ?)" +} + +fsck_ask_err() { + warn "*** An error occurred during the file system check." + warn "*** Dropping you to a shell; the system will try" + warn "*** to mount the filesystem(s), when you leave the shell." + emergency_shell -n "(Repair filesystem)" +} + +# inherits: _ret _drv _out +fsck_tail() { + [ "$_ret" -gt 0 ] && warn "$_drv returned with $_ret" + if [ "$_ret" -ge 4 ]; then + [ -n "$_out" ] && echo "$_out" | vwarn + fsck_ask_err + else + [ -n "$_out" ] && echo "$_out" | vinfo + [ "$_ret" -ge 2 ] && fsck_ask_reboot + fi +} + +# note: this function sets _drv of the caller +fsck_able() { + case "$1" in + xfs) + # { + # type xfs_db && + # type xfs_repair && + # type xfs_check && + # type mount && + # type umount + # } >/dev/null 2>&1 && + # _drv="_drv=none fsck_drv_xfs" && + # return 0 + return 1 + ;; + ext?) + type e2fsck > /dev/null 2>&1 \ + && _drv="fsck_drv_com e2fsck" \ + && return 0 + ;; + f2fs) + type fsck.f2fs > /dev/null 2>&1 \ + && _drv="fsck_drv_com fsck.f2fs" \ + && return 0 + ;; + jfs) + type jfs_fsck > /dev/null 2>&1 \ + && _drv="fsck_drv_com jfs_fsck" \ + && return 0 + ;; + reiserfs) + type reiserfsck > /dev/null 2>&1 \ + && _drv="fsck_drv_com reiserfsck" \ + && return 0 + ;; + btrfs) + # type btrfsck >/dev/null 2>&1 && + # _drv="_drv=none fsck_drv_btrfs" && + # return 0 + return 1 + ;; + nfs*) + # nfs can be a nop, returning success + _drv=":" \ + && return 0 + ;; + *) + type fsck > /dev/null 2>&1 \ + && _drv="fsck_drv_std fsck" \ + && return 0 + ;; + esac + + return 1 +} + +# note: all drivers inherit: _drv _fop _dev + +fsck_drv_xfs() { + # xfs fsck is not necessary... Either it mounts or not + return 0 +} + +fsck_drv_btrfs() { + # btrfs fsck is not necessary... Either it mounts or not + return 0 +} + +# common code for checkers that follow usual subset of options and return codes +fsck_drv_com() { + local _drv="$1" + local _ret + local _out + + if ! strglobin "$_fop" "-[ynap]"; then + _fop="-a${_fop:+ "$_fop"}" + fi + + info "issuing $_drv $_fop $_dev" + # we enforce non-interactive run, so $() is fine + # shellcheck disable=SC2086 + _out=$($_drv $_fop "$_dev") + _ret=$? + fsck_tail + + return $_ret +} + +# code for generic fsck, if the filesystem checked is "unknown" to us +fsck_drv_std() { + local _ret + local _out + unset _out + + info "issuing fsck $_fop $_dev" + # note, we don't enforce -a here, thus fsck is being run (in theory) + # interactively; otherwise some tool might complain about lack of terminal + # (and using -a might not be safe) + # shellcheck disable=SC2086 + fsck $_fop "$_dev" > /dev/console 2>&1 + _ret=$? + fsck_tail + + return $_ret +} + +# checks single filesystem, relying on specific "driver"; we don't rely on +# automatic checking based on fstab, so empty one is passed; +# takes 4 arguments - device, filesystem, filesystem options, additional fsck options; +# first 2 arguments are mandatory (fs may be auto or "") +# returns 255 if filesystem wasn't checked at all (e.g. due to lack of +# necessary tools or insufficient options) +fsck_single() { + local FSTAB_FILE=/etc/fstab.empty + local _dev="$1" + local _fs="${2:-auto}" + local _fop="$4" + local _drv + + [ $# -lt 2 ] && return 255 + _dev=$(readlink -f "$(label_uuid_to_dev "$_dev")") + [ -e "$_dev" ] || return 255 + _fs=$(det_fs "$_dev" "$_fs") + fsck_able "$_fs" || return 255 + + info "Checking $_fs: $_dev" + export FSTAB_FILE + eval "$_drv" + return $? +} + +# takes list of filesystems to check in parallel; we don't rely on automatic +# checking based on fstab, so empty one is passed +fsck_batch() { + local FSTAB_FILE=/etc/fstab.empty + local _drv=fsck + local _dev + local _ret + local _out + + [ $# -eq 0 ] || ! type fsck > /dev/null 2>&1 && return 255 + + info "Checking filesystems (fsck -M -T -a):" + for _dev in "$@"; do + info " $_dev" + done + + export FSTAB_FILE + _out="$(fsck -M -T "$@" -- -a)" + _ret=$? + + fsck_tail + + return $_ret +} + +# verify supplied filesystem type: +# if user provided the fs and we couldn't find it, assume user is right +# if we found the fs, assume we're right +det_fs() { + local _dev="$1" + local _orig="${2:-auto}" + local _fs + + _fs=$(udevadm info --query=property --name="$_dev" \ + | while read -r line || [ -n "$line" ]; do + if str_starts "$line" "ID_FS_TYPE="; then + echo "${line#ID_FS_TYPE=}" + break + fi + done) + _fs=${_fs:-auto} + + if [ "$_fs" = "auto" ]; then + _fs="$_orig" + fi + echo "$_fs" +} + +write_fs_tab() { + local _o + local _rw + local _root + local _rootfstype + local _rootflags + local _fspassno + + _fspassno="0" + _root="$1" + _rootfstype="$2" + _rootflags="$3" + [ -z "$_rootfstype" ] && _rootfstype=$(getarg rootfstype=) + [ -z "$_rootflags" ] && _rootflags=$(getarg rootflags=) + + [ -z "$_rootfstype" ] && _rootfstype="auto" + + if [ -z "$_rootflags" ]; then + _rootflags="ro,x-initrd.mount" + else + _rootflags="ro,$_rootflags,x-initrd.mount" + fi + + _rw=0 + + CMDLINE=$(getcmdline) + for _o in $CMDLINE; do + case $_o in + rw) + _rw=1 + ;; + ro) + _rw=0 + ;; + esac + done + if [ "$_rw" = "1" ]; then + _rootflags="$_rootflags,rw" + if ! getargbool 0 rd.skipfsck; then + _fspassno="1" + fi + fi + + if grep -q "$_root /sysroot" /etc/fstab; then + echo "$_root /sysroot $_rootfstype $_rootflags $_fspassno 0" >> /etc/fstab + else + return + fi + + if type systemctl > /dev/null 2> /dev/null; then + systemctl daemon-reload + systemctl --no-block start initrd-root-fs.target + fi +} diff --git a/modules.d/99fs-lib/module-setup.sh b/modules.d/99fs-lib/module-setup.sh new file mode 100755 index 0000000..2b81b0c --- /dev/null +++ b/modules.d/99fs-lib/module-setup.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +# called by dracut +check() { + return 0 +} + +# called by dracut +depends() { + return 0 +} + +echo_fs_helper() { + local fs=$2 + case "$fs" in + xfs) + echo -n " xfs_db xfs_repair xfs_check xfs_metadump " + ;; + ext?) + echo -n " e2fsck " + ;; + jfs) + echo -n " jfs_fsck " + ;; + reiserfs) + echo -n " reiserfsck " + ;; + btrfs) + echo -n " btrfsck " + ;; + esac + + echo -n " fsck.$fs " + return 0 +} + +include_fs_helper_modules() { + local fs=$2 + case "$fs" in + xfs | btrfs | ext4 | ext3) + instmods crc32c + ;; + f2fs) + instmods crc32 + ;; + esac +} + +# called by dracut +installkernel() { + # xfs/btrfs/ext4 need crc32c, f2fs needs crc32 + if [[ $hostonly ]]; then + for_each_host_dev_fs include_fs_helper_modules + : + else + instmods crc32c crc32 + fi +} + +# called by dracut +install() { + local _helpers + + inst "$moddir/fs-lib.sh" "/lib/fs-lib.sh" + : > "${initdir}"/etc/fstab.empty + + [[ $nofscks == "yes" ]] && return + + if [[ $fscks == "${fscks#*[^ ]*}" ]]; then + _helpers=( + /sbin/fsck* /usr/sbin/fsck* + xfs_db xfs_check xfs_repair xfs_metadump + e2fsck jfs_fsck reiserfsck btrfsck + ) + if [[ $hostonly ]]; then + read -r -a _helpers < <(for_each_host_dev_fs echo_fs_helper) + fi + else + read -r -a _helpers <<< "$fscks" + fi + + _helpers+=(umount mount) + + if [[ ${_helpers[*]} == *e2fsck* ]] && [[ -e $dracutsysrootdir/etc/e2fsck.conf ]]; then + inst_simple /etc/e2fsck.conf + fi + + inst_multiple -o "${_helpers[@]}" fsck +} diff --git a/modules.d/99img-lib/img-lib.sh b/modules.d/99img-lib/img-lib.sh new file mode 100755 index 0000000..4700832 --- /dev/null +++ b/modules.d/99img-lib/img-lib.sh @@ -0,0 +1,108 @@ +#!/bin/sh +# img-lib.sh: utilities for dealing with archives and filesystem images. +# +# TODO: identify/unpack rpm, deb, maybe others? + +# super-simple "file" that only identifies archives. +# works with stdin if $1 is not set. +det_archive() { + # NOTE: internal echo -e works in ash and bash, but not dash + local bz xz gz zs + local headerblock + bz="BZh" + # shellcheck disable=SC3037 + xz="$(/bin/echo -e '\xfd7zXZ')" + # shellcheck disable=SC3037 + gz="$(/bin/echo -e '\x1f\x8b')" + # shellcheck disable=SC3037 + zs="$(/bin/echo -e '\x28\xB5\x2F\xFD')" + headerblock="$(dd ${1:+if=$1} bs=262 count=1 2> /dev/null | tr -d '\0')" + case "$headerblock" in + $xz*) echo "xz" ;; + $gz*) echo "gzip" ;; + $bz*) echo "bzip2" ;; + $zs*) echo "zstd" ;; + 07070*) echo "cpio" ;; + *ustar) echo "tar" ;; + esac +} + +# determine filesystem type for a filesystem image +det_fs_img() { + local dev + dev=$(losetup --find --show "$1") rv="" + det_fs "$dev" + rv=$? + losetup -d "$dev" + return $rv +} + +# unpack_archive ARCHIVE OUTDIR +# unpack a (possibly compressed) cpio/tar archive +unpack_archive() { + local img="$1" outdir="$2" archiver="" decompr="" + local ft + ft="$(det_archive "$img")" + case "$ft" in + xz | gzip | bzip2 | zstd) decompr="$ft -dc" ;; + cpio | tar) decompr="cat" ;; + *) return 1 ;; + esac + ft="$($decompr "$img" | det_archive)" + case "$ft" in + cpio) archiver="cpio -iumd" ;; + tar) archiver="tar -xf -" ;; + *) return 2 ;; + esac + mkdir -p "$outdir" + ( + cd "$outdir" || exit + $decompr | $archiver 2> /dev/null + ) < "$img" +} + +# unpack_fs FSIMAGE OUTDIR +# unpack a filesystem image +unpack_fs() { + local img="$1" outdir="$2" + local mnt + mnt="$(mkuniqdir /tmp unpack_fs.)" + mount -o loop "$img" "$mnt" || { + rmdir "$mnt" + return 1 + } + mkdir -p "$outdir" + outdir="$( + cd "$outdir" || exit + pwd + )" + copytree "$mnt" "$outdir" + umount "$mnt" + rmdir "$mnt" +} + +# unpack an image file - compressed/uncompressed cpio/tar, filesystem, whatever +# unpack_img IMAGEFILE OUTDIR +unpack_img() { + local img="$1" outdir="$2" + [ -r "$img" ] || { + warn "can't read img!" + return 1 + } + [ -n "$outdir" ] || { + warn "unpack_img: no output dir given" + return 1 + } + + if [ "$(det_archive "$img")" ]; then + unpack_archive "$@" || { + warn "can't unpack archive file!" + return 1 + } + else + unpack_fs "$@" || { + warn "can't unpack filesystem image!" + return 1 + } + fi +} diff --git a/modules.d/99img-lib/module-setup.sh b/modules.d/99img-lib/module-setup.sh new file mode 100755 index 0000000..4f57b37 --- /dev/null +++ b/modules.d/99img-lib/module-setup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# module-setup for img-lib + +# called by dracut +check() { + require_binaries tar gzip dd echo tr || return 1 + return 255 +} + +# called by dracut +depends() { + return 0 +} + +# called by dracut +install() { + inst_multiple tar gzip dd echo tr rmdir + # TODO: make this conditional on a cmdline flag / config option + inst_multiple -o cpio xz bzip2 zstd + inst_simple "$moddir/img-lib.sh" "/lib/img-lib.sh" +} diff --git a/modules.d/99memstrack/memstrack-report.sh b/modules.d/99memstrack/memstrack-report.sh new file mode 100755 index 0000000..0ee7f2f --- /dev/null +++ b/modules.d/99memstrack/memstrack-report.sh @@ -0,0 +1,17 @@ +#!/bin/sh +. /lib/dracut-lib.sh + +if ! [ "$DEBUG_MEM_LEVEL" -ge 4 ]; then + return 0 +fi + +if command -v systemctl > /dev/null; then + systemctl stop memstrack.service +else + pkill --signal INT '[m]emstrack' + while pgrep -c '[m]emstrack' > /dev/null; do + sleep 1 + done +fi + +cat /.memstrack diff --git a/modules.d/99memstrack/memstrack-start.sh b/modules.d/99memstrack/memstrack-start.sh new file mode 100755 index 0000000..45f65c2 --- /dev/null +++ b/modules.d/99memstrack/memstrack-start.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Mount kernel debug fs so debug tools can work. +# memdebug=4 and memdebug=5 requires debug fs to be mounted. +# And there is no need to umount it. + +type getargnum > /dev/null 2>&1 || . /lib/dracut-lib.sh + +# "sys/kernel/tracing" has the priority if exists. +get_trace_base() { + # trace access through debugfs would be obsolete if "/sys/kernel/tracing" is available. + if [ -d "/sys/kernel/tracing" ]; then + echo "/sys/kernel" + else + echo "/sys/kernel/debug" + fi +} + +is_debugfs_ready() { + [ -f "$(get_trace_base)/tracing/trace" ] +} + +prepare_debugfs() { + trace_base=$(get_trace_base) + # old debugfs interface case. + if ! [ -d "$trace_base/tracing" ]; then + mount none -t debugfs "$trace_base" + # new tracefs interface case. + elif ! [ -f "$trace_base/tracing/trace" ]; then + mount none -t tracefs "$trace_base/tracing" + fi + + if ! [ -f "$trace_base/tracing/trace" ]; then + echo "WARN: failed to mount debugfs" + return 1 + fi +} + +if ! is_debugfs_ready; then + prepare_debugfs +fi + +if [ -n "$DEBUG_MEM_LEVEL" ]; then + if [ "$DEBUG_MEM_LEVEL" -ge 5 ]; then + echo "memstrack - will report kernel module memory usage summary and top allocation stack" + nohup memstrack --report module_summary,module_top --notui --throttle 80 -o /.memstrack > /dev/null & + elif [ "$DEBUG_MEM_LEVEL" -ge 4 ]; then + echo "memstrack - will report memory usage summary" + nohup memstrack --report module_summary --notui --throttle 80 -o /.memstrack > /dev/null & + else + exit 0 + fi +fi + +PID=$! +RET=$? + +if [ $RET -ne 0 ]; then + echo "Failed to start memstrack, exit status: $RET" + exit $RET +fi + +echo $PID > /run/memstrack.pid + +# Wait a second for memstrack to setup everything, avoid missing any event +sleep 1 diff --git a/modules.d/99memstrack/memstrack.service b/modules.d/99memstrack/memstrack.service new file mode 100644 index 0000000..3717c73 --- /dev/null +++ b/modules.d/99memstrack/memstrack.service @@ -0,0 +1,15 @@ +[Unit] +Description=Memstrack Anylazing Service +DefaultDependencies=no +Before=dracut-cmdline.service systemd-udevd.service local-fs-pre.target +IgnoreOnIsolate=true +ConditionKernelCommandLine=|rd.memdebug=4 +ConditionKernelCommandLine=|rd.memdebug=5 + +[Service] +Type=forking +ExecStart=/bin/memstrack-start +PIDFile=/run/memstrack.pid +StandardInput=null +StandardOutput=journal+console +StandardError=journal+console diff --git a/modules.d/99memstrack/module-setup.sh b/modules.d/99memstrack/module-setup.sh new file mode 100755 index 0000000..27563eb --- /dev/null +++ b/modules.d/99memstrack/module-setup.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +check() { + if ! require_binaries pgrep pkill memstrack; then + dinfo "memstrack is not available" + dinfo "If you need to use rd.memdebug>=4, please install memstrack and procps-ng" + return 1 + fi + + return 0 +} + +depends() { + echo systemd + return 0 +} + +install() { + inst_multiple pgrep pkill nohup + inst "/bin/memstrack" "/bin/memstrack" + + inst "$moddir/memstrack-start.sh" "/bin/memstrack-start" + inst_hook cleanup 99 "$moddir/memstrack-report.sh" + + inst "$moddir/memstrack.service" "$systemdsystemunitdir/memstrack.service" + + $SYSTEMCTL -q --root "$initdir" add-wants initrd.target memstrack.service +} diff --git a/modules.d/99shutdown/module-setup.sh b/modules.d/99shutdown/module-setup.sh new file mode 100755 index 0000000..2b99902 --- /dev/null +++ b/modules.d/99shutdown/module-setup.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# called by dracut +check() { + return 0 +} + +# called by dracut +depends() { + echo base + return 0 +} + +# called by dracut +install() { + local _d + inst_multiple umount poweroff reboot halt losetup stat sleep timeout + inst_multiple -o kexec + inst "$moddir/shutdown.sh" "$prefix/shutdown" + [ -e "${initdir}/lib" ] || mkdir -m 0755 -p "${initdir}"/lib + mkdir -m 0755 -p "${initdir}"/lib/dracut + mkdir -m 0755 -p "${initdir}"/lib/dracut/hooks + for _d in $hookdirs shutdown shutdown-emergency; do + mkdir -m 0755 -p "${initdir}"/lib/dracut/hooks/"$_d" + done +} diff --git a/modules.d/99shutdown/shutdown.sh b/modules.d/99shutdown/shutdown.sh new file mode 100755 index 0000000..d611d44 --- /dev/null +++ b/modules.d/99shutdown/shutdown.sh @@ -0,0 +1,176 @@ +#!/bin/sh +# +# Licensed under the GPLv2 +# +# Copyright 2011, Red Hat, Inc. +# Harald Hoyer <harald@redhat.com> +ACTION="$1" + +# Before trying to use /dev/console, verify that it exists, +# and that it can actually be used. When console=null is used, +# echo will fail. We do the check in a subshell, because otherwise +# the process will be killed when when running as PID 1. +# shellcheck disable=SC2217 +[ -w /dev/console ] \ + && (echo < /dev/console > /dev/null 2> /dev/null) \ + && exec < /dev/console >> /dev/console 2>> /dev/console + +export TERM=linux +export PATH=/usr/sbin:/usr/bin:/sbin:/bin +. /lib/dracut-lib.sh + +if [ "$(stat -c '%T' -f /)" = "tmpfs" ]; then + mount -o remount,rw / +fi + +mkdir -p /oldsys +for i in sys proc run dev; do + mkdir -p /oldsys/$i + mount --move /oldroot/$i /oldsys/$i +done + +# if "kexec" was installed after creating the initramfs, we try to copy it from the real root +# libz normally is pulled in via kmod/modprobe and udevadm +if [ "$ACTION" = "kexec" ] && ! command -v kexec > /dev/null 2>&1; then + for p in /usr/sbin /usr/bin /sbin /bin; do + cp -a /oldroot/${p}/kexec $p > /dev/null 2>&1 && break + done + hash kexec +fi + +trap "emergency_shell --shutdown shutdown Signal caught!" 0 +getarg 'rd.break=pre-shutdown' && emergency_shell --shutdown pre-shutdown "Break before pre-shutdown" + +source_hook pre-shutdown + +warn "Killing all remaining processes" + +killall_proc_mountpoint /oldroot || sleep 0.2 + +# Timeout for umount calls. The value can be set to 0 to wait forever. +_umount_timeout=$(getarg rd.shutdown.timeout.umount) +_umount_timeout=${_umount_timeout:-90s} +_timed_out_umounts="" + +umount_a() { + local _verbose="n" + if [ "$1" = "-v" ]; then + _verbose="y" + shift + exec 7>&2 + else + exec 7> /dev/null + fi + + local _did_umount="n" + while read -r _ mp _ || [ -n "$mp" ]; do + strstr "$mp" oldroot || continue + strstr "$_timed_out_umounts" " $mp " && continue + + # Unmount the file system. The operation uses a timeout to avoid waiting + # indefinitely if this is e.g. a stuck NFS mount. The command is + # invoked in a subshell to silence also the "Killed" message that might + # be produced by the shell. + ( + set +m + timeout --signal=KILL "$_umount_timeout" umount "$mp" + ) 2>&7 + local ret=$? + if [ $ret -eq 0 ]; then + _did_umount="y" + warn "Unmounted $mp." + elif [ $ret -eq 137 ]; then + _timed_out_umounts="$_timed_out_umounts $mp " + warn "Unmounting $mp timed out." + elif [ "$_verbose" = "y" ]; then + warn "Unmounting $mp failed with status $ret." + fi + done < /proc/mounts + + losetup -D 2>&7 + + exec 7>&- + [ "$_did_umount" = "y" ] && return 0 + return 1 +} + +_cnt=0 +while [ $_cnt -le 40 ]; do + umount_a || break + _cnt=$((_cnt + 1)) +done + +[ $_cnt -ge 40 ] && umount_a -v + +if strstr "$(cat /proc/mounts)" "/oldroot"; then + warn "Cannot umount /oldroot" + for _pid in /proc/*; do + _pid=${_pid##/proc/} + case $_pid in + *[!0-9]*) continue ;; + esac + [ "$_pid" -eq $$ ] && continue + + [ -e "/proc/$_pid/exe" ] || continue + [ -e "/proc/$_pid/root" ] || continue + + if strstr "$(ls -l /proc/"$_pid" /proc/"$_pid"/fd 2> /dev/null)" "oldroot"; then + warn "Blocking umount of /oldroot [$_pid] $(cat /proc/"$_pid"/cmdline)" + else + warn "Still running [$_pid] $(cat /proc/"$_pid"/cmdline)" + fi + + # shellcheck disable=SC2012 + ls -l "/proc/$_pid/exe" 2>&1 | vwarn + # shellcheck disable=SC2012 + ls -l "/proc/$_pid/fd" 2>&1 | vwarn + done +fi + +_check_shutdown() { + local __f + local __s=0 + for __f in "$hookdir"/shutdown/*.sh; do + [ -e "$__f" ] || continue + # shellcheck disable=SC1090 disable=SC2240 + if (final="$1" . "$__f" "$1"); then + rm -f -- "$__f" + else + __s=1 + fi + done + return $__s +} + +_cnt=0 +while [ $_cnt -le 40 ]; do + _check_shutdown && break + _cnt=$((_cnt + 1)) +done +[ $_cnt -ge 40 ] && _check_shutdown final + +if type plymouth > /dev/null 2>&1; then + plymouth --hide-splash +elif [ -x /oldroot/bin/plymouth ]; then + /oldroot/bin/plymouth --hide-splash +fi + +getarg 'rd.break=shutdown' && emergency_shell --shutdown shutdown "Break before shutdown" + +case "$ACTION" in + reboot | poweroff | halt) + $ACTION -f -n + warn "$ACTION failed!" + ;; + kexec) + kexec -e + warn "$ACTION failed!" + reboot -f -n + ;; + *) + warn "Shutdown called with argument '$ACTION'. Rebooting!" + reboot -f -n + ;; +esac + +emergency_shell --shutdown shutdown diff --git a/modules.d/99squash/init-squash.sh b/modules.d/99squash/init-squash.sh new file mode 100755 index 0000000..59769f6 --- /dev/null +++ b/modules.d/99squash/init-squash.sh @@ -0,0 +1,34 @@ +#!/bin/sh +PATH=/bin:/sbin + +[ -e /proc/self/mounts ] \ + || (mkdir -p /proc && mount -t proc -o nosuid,noexec,nodev proc /proc) + +grep -q '^sysfs /sys sysfs' /proc/self/mounts \ + || (mkdir -p /sys && mount -t sysfs -o nosuid,noexec,nodev sysfs /sys) + +grep -q '^devtmpfs /dev devtmpfs' /proc/self/mounts \ + || (mkdir -p /dev && mount -t devtmpfs -o mode=755,noexec,nosuid,strictatime devtmpfs /dev) + +grep -q '^tmpfs /run tmpfs' /proc/self/mounts \ + || (mkdir -p /run && mount -t tmpfs -o mode=755,noexec,nosuid,strictatime tmpfs /run) + +# Load required modules +modprobe loop +modprobe squashfs +modprobe overlay + +# Mount the squash image +mount -t ramfs ramfs /squash +mkdir -p /squash/root /squash/overlay/upper /squash/overlay/work +mount -t squashfs -o ro,loop /squash-root.img /squash/root + +# Setup new root overlay +mkdir /newroot +mount -t overlay overlay -o lowerdir=/squash/root,upperdir=/squash/overlay/upper,workdir=/squash/overlay/work/ /newroot/ + +# Move all mount points to new root to prepare chroot +mount --move /squash /newroot/squash + +# Jump to new root and clean setup files +SYSTEMD_IN_INITRD=lenient exec switch_root /newroot /init diff --git a/modules.d/99squash/module-setup.sh b/modules.d/99squash/module-setup.sh new file mode 100755 index 0000000..dc2e0a2 --- /dev/null +++ b/modules.d/99squash/module-setup.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +check() { + require_binaries mksquashfs unsquashfs || return 1 + require_kernel_modules squashfs loop overlay || return 1 + + return 255 +} + +depends() { + echo "systemd-initrd" + return 0 +} + +installpost() { + local _busybox + _busybox=$(find_binary busybox) + + # Move everything under $initdir except $squash_dir + # itself into squash image + for i in "$initdir"/*; do + [[ $squash_dir == "$i"/* ]] || mv "$i" "$squash_dir"/ + done + + # Create mount points for squash loader + mkdir -p "$initdir"/squash/ + mkdir -p "$squash_dir"/squash/ + + # Copy dracut spec files out side of the squash image + # so dracut rebuild and lsinitrd can work + for file in "$squash_dir"/usr/lib/dracut/*; do + [[ -f $file ]] || continue + DRACUT_RESOLVE_DEPS=1 dracutsysrootdir="$squash_dir" inst "${file#"$squash_dir"}" + done + + # Install required modules and binaries for the squash image init script. + if [[ $_busybox ]]; then + inst "$_busybox" /usr/bin/busybox + for _i in sh echo mount modprobe mkdir switch_root grep umount; do + ln_r /usr/bin/busybox /usr/bin/$_i + done + else + DRACUT_RESOLVE_DEPS=1 inst_multiple sh mount modprobe mkdir switch_root grep umount + + # libpthread workaround: pthread_cancel wants to dlopen libgcc_s.so + inst_libdir_file -o "libgcc_s.so*" + + # FIPS workaround for Fedora/RHEL: libcrypto needs libssl when FIPS is enabled + [[ $DRACUT_FIPS_MODE ]] && inst_libdir_file -o "libssl.so*" + fi + + hostonly="" instmods "loop" "squashfs" "overlay" + dracut_kernel_post + + # Install squash image init script. + ln_r /usr/bin /bin + ln_r /usr/sbin /sbin + inst_simple "$moddir"/init-squash.sh /init + + # make sure that library links are correct and up to date for squash loader + build_ld_cache +} + +install() { + if [[ $DRACUT_SQUASH_POST_INST ]]; then + installpost + fi +} diff --git a/modules.d/99uefi-lib/module-setup.sh b/modules.d/99uefi-lib/module-setup.sh new file mode 100755 index 0000000..6deeee8 --- /dev/null +++ b/modules.d/99uefi-lib/module-setup.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# called by dracut +check() { + return 255 +} + +# called by dracut +depends() { + echo bash + return 0 +} + +# called by dracut +install() { + inst_simple "$moddir/uefi-lib.sh" "/lib/uefi-lib.sh" +} diff --git a/modules.d/99uefi-lib/uefi-lib.sh b/modules.d/99uefi-lib/uefi-lib.sh new file mode 100755 index 0000000..06004ee --- /dev/null +++ b/modules.d/99uefi-lib/uefi-lib.sh @@ -0,0 +1,162 @@ +#!/bin/bash +# +# Copyright 2013 Red Hat, Inc. All rights reserved. +# Copyright 2013 Harald Hoyer <harald@redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +getbyte() { + local IFS= LC_CTYPE=C res c=0 + read -r -n 1 -d '' c + res=$? + # the single quote in the argument of the printf + # yields the numeric value of $c (ASCII since LC_CTYPE=C) + [[ -n $c ]] && c=$(printf '%u' "'$c") || c=0 + printf "%s" "$c" + return $res +} + +getword() { + local b1=0 b2=0 val=0 + b1=$(getbyte) + b2=$(getbyte) + ((val = b2 * 256 + b1)) + echo "$val" + return 0 +} + +# E.g. Acpi(PNP0A08,0x0)/Pci(0x3,0x0)/Pci(0x0,0x0)/MAC(90E2BA265ED4,0x0)/Vlan(172)/Fibre(0x4EA06104A0CC0050,0x0) +uefi_device_path() { + data=${1:-/sys/firmware/efi/efivars/FcoeBootDevice-a0ebca23-5f9c-447a-a268-22b6c158c2ac} + [ -f "$data" ] || return 1 + + local IFS= LC_CTYPE=C res tt len type hextype first + first=1 + { + getword > /dev/null + getword > /dev/null + while :; do + type=$(getbyte) || return 1 + subtype=$(getbyte) || return 1 + len=$(getword) || return 1 + hextype=$(printf "%02x%02x" "$type" "$subtype") + if [[ $first == 1 ]]; then + first=0 + elif [[ $hextype != "7fff" ]]; then + printf "/" + fi + case $hextype in + 0101) + # PCI + tt=$(getword) + printf "PCI(0x%x,0x%x)" $((tt / 256)) $((tt & 255)) + ;; + 0201) + # ACPI + printf "Acpi(0x%x,0x%x)" $(($(getword) + $(getword) * 65536)) $(($(getword) + $(getword) * 65536)) + ;; + 0303) + # FIBRE + getword &> /dev/null + getword &> /dev/null + printf "Fibre(0x%x%x%x%x%x%x%x%x,0x%x)" \ + "$(getbyte)" "$(getbyte)" "$(getbyte)" "$(getbyte)" \ + "$(getbyte)" "$(getbyte)" "$(getbyte)" "$(getbyte)" \ + "$(($(getword) + $(getword) * 65536 + 4294967296 * ($(getword) + $(getword) * 65536)))" + ;; + 030b) + # MAC + printf "MAC(%02x%02x%02x%02x%02x%02x," "$(getbyte)" "$(getbyte)" "$(getbyte)" "$(getbyte)" "$(getbyte)" "$(getbyte)" + for ((i = 0; i < 26; i++)); do tt=$(getbyte) || return 1; done + printf "0x%x)" "$(getbyte)" + ;; + 0314) + # VLAN + printf "VLAN(%d)" "$(getword)" + ;; + 7fff) + # END + printf "\n" + return 0 + ;; + *) + #printf "Unknown(Type:0x%02x SubType:0x%02x len=%d)\n" "$type" "$subtype" "$len" >&2 + for ((i = 0; i < len - 4; i++)); do tt=$(getbyte); done + ;; + esac + done + } < "$data" +} + +get_fcoe_boot_mac() { + data=${1:-/sys/firmware/efi/efivars/FcoeBootDevice-a0ebca23-5f9c-447a-a268-22b6c158c2ac} + [ -f "$data" ] || return 1 + local IFS= LC_CTYPE=C tt len type hextype + { + getword > /dev/null + getword > /dev/null + while :; do + type=$(getbyte) || return 1 + subtype=$(getbyte) || return 1 + len=$(getword) || return 1 + hextype=$(printf "%02x%02x" "$type" "$subtype") + case $hextype in + 030b) + # MAC + printf "%02x:%02x:%02x:%02x:%02x:%02x" "$(getbyte)" "$(getbyte)" "$(getbyte)" "$(getbyte)" "$(getbyte)" "$(getbyte)" + for ((i = 0; i < 27; i++)); do tt=$(getbyte) || return 1; done + ;; + 7fff) + # END + return 0 + ;; + *) + #printf "Unknown(Type:0x%02x SubType:0x%02x len=%d)\n" "$type" "$subtype" "$len" >&2 + for ((i = 0; i < len - 4; i++)); do tt=$(getbyte); done + ;; + esac + done + } < "$data" +} + +get_fcoe_boot_vlan() { + data=${1:-/sys/firmware/efi/efivars/FcoeBootDevice-a0ebca23-5f9c-447a-a268-22b6c158c2ac} + [ -f "$data" ] || return 1 + local IFS= LC_CTYPE=C tt len type hextype + { + getword > /dev/null + getword > /dev/null + while :; do + type=$(getbyte) || return 1 + subtype=$(getbyte) || return 1 + len=$(getword) || return 1 + hextype=$(printf "%02x%02x" "$type" "$subtype") + case $hextype in + 0314) + # VLAN + printf "%d" "$(getword)" + ;; + 7fff) + # END + return 0 + ;; + *) + #printf "Unknown(Type:0x%02x SubType:0x%02x len=%d)\n" "$type" "$subtype" "$len" >&2 + for ((i = 0; i < len; i++)); do tt=$(getbyte); done + ;; + esac + done + } < "$data" +} |