diff --git a/Makefile b/Makefile index 45b619a..5b22b4a 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +SHELL := /bin/bash prefix := /usr libdir := $(prefix)/lib @@ -7,26 +8,73 @@ systemdunitsdir := $(shell pkg-config --define-variable=prefix=$(prefix) --varia || echo $(libdir)/systemd/system/) sysctldir := $(shell pkg-config --define-variable=prefix=$(prefix) --variable=sysctldir systemd 2>/dev/null \ || echo $(libdir)/sysctl.d/) -dracutmodulesdir := $(shell pkg-config --define-variable=prefix=$(prefix) --variable=dracutmodulesdir dracut 2>/dev/null \ - || echo $(libdir)/dracut/modules.d/) +alpmhooksdir := $(shell echo $(sharedir)/libalpm/hooks/) kdump-load.sh: kdump-load.header common.sh kdump-load.sh.in cat $^ > $@ -module-setup.sh: module-setup.header common.sh module-setup.sh.in - cat $^ > $@ - save-dumps.sh: save-dumps.header common.sh save-dumps.sh.in cat $^ > $@ -all: kdump-load.sh module-setup.sh save-dumps.sh +99-kdump-dracut.hook: initramfs/alpm-hook.generic + sed 's/INITRD/dracut/g' $^ > initramfs/$@ + +kdump-dracut-hook.sh: common.sh initramfs/dracut/dracut-common.sh + sed 's/INITRD/dracut/g' initramfs/alpm-script.header > initramfs/alpm-header-dracut + sed 's/INITRD/dracut/g' initramfs/alpm-script.sh.in > initramfs/alpm-script.sh.in-dracut + cat initramfs/alpm-header-dracut $^ initramfs/alpm-script.sh.in-dracut > initramfs/dracut/$@ + rm -f initramfs/alpm-header-dracut initramfs/alpm-script.sh.in-dracut + +module-setup.sh: initramfs/dracut/module-setup.header common.sh initramfs/dracut/module-setup.sh.in + cat $^ > initramfs/dracut/$@ + +.PHONY: dracut +dracut: 99-kdump-dracut.hook kdump-dracut-hook.sh module-setup.sh + +99-kdump-mkinitcpio.hook: initramfs/alpm-hook.generic + sed 's/INITRD/mkinitcpio/g' $^ > initramfs/$@ + +99-kdump-mkinitcpio-git.hook: initramfs/alpm-hook.generic + sed 's/INITRD/mkinitcpio-git/g' $^ > initramfs/$@ + +kdump-mkinitcpio-hook.sh: common.sh initramfs/initcpio/initcpio-common.sh + sed 's/INITRD/mkinitcpio/g' initramfs/alpm-script.header > initramfs/alpm-header-initcpio + sed 's/INITRD/mkinitcpio/g' initramfs/alpm-script.sh.in > initramfs/alpm-script.sh.in-initcpio + cat initramfs/alpm-header-initcpio $^ initramfs/alpm-script.sh.in-initcpio > initramfs/initcpio/$@ + rm -f initramfs/alpm-header-initcpio initramfs/alpm-script.sh.in-initcpio + +kdump.hook: initramfs/kdump-collect.sh + sed 's/\#ENTRY POINT/run_hook() \{/g' $^ > initramfs/initcpio/$@ + sed -i 's/\#END/\}/g' initramfs/initcpio/$@ + +kdump.install: initramfs/initcpio/kdump.install.header common.sh initramfs/initcpio/kdump.install.in + cat $^ > initramfs/initcpio/$@ + +.PHONY: mkinitcpio +mkinitcpio: 99-kdump-mkinitcpio.hook 99-kdump-mkinitcpio-git.hook kdump-mkinitcpio-hook.sh kdump.hook kdump.install + +all: kdump-load.sh save-dumps.sh dracut mkinitcpio install: all install -D -m0644 kdump-init.service $(DESTDIR)$(systemdunitsdir)/kdump-init.service install -D -m0644 20-panic-sysctls.conf $(DESTDIR)$(sysctldir)/20-panic-sysctls.conf - install -D -m0755 kdump-collect.sh $(DESTDIR)$(dracutmodulesdir)/55kdump/kdump-collect.sh - install -D -m0755 module-setup.sh $(DESTDIR)$(dracutmodulesdir)/55kdump/module-setup.sh - install -D -m0644 README.md $(DESTDIR)$(dracutmodulesdir)/55kdump/README + install -D -m0644 README.md $(DESTDIR)$(libdir)/kdump/README.md install -D -m0755 kdump-load.sh $(DESTDIR)$(libdir)/kdump/kdump-load.sh install -D -m0755 save-dumps.sh $(DESTDIR)$(libdir)/kdump/save-dumps.sh install -D -m0644 00-default.conf $(DESTDIR)$(sharedir)/kdump.d/00-default + install -D -m0644 initramfs/99-kdump-dracut.hook $(DESTDIR)$(alpmhooksdir)/99-kdump-dracut.hook + install -D -m0644 initramfs/99-kdump-mkinitcpio.hook $(DESTDIR)$(alpmhooksdir)/99-kdump-mkinitcpio.hook + install -D -m0644 initramfs/99-kdump-mkinitcpio-git.hook $(DESTDIR)$(alpmhooksdir)/99-kdump-mkinitcpio-git.hook + install -D -m0755 initramfs/dracut/kdump-dracut-hook.sh $(DESTDIR)$(libdir)/kdump/kdump-dracut-hook.sh + install -D -m0755 initramfs/kdump-collect.sh $(DESTDIR)$(libdir)/kdump/dracut/kdump-collect.sh + install -D -m0755 initramfs/dracut/module-setup.sh $(DESTDIR)$(libdir)/kdump/dracut/module-setup.sh + install -D -m0755 initramfs/initcpio/kdump-mkinitcpio-hook.sh $(DESTDIR)$(libdir)/kdump/kdump-mkinitcpio-hook.sh + install -D -m0755 initramfs/initcpio/kdump-mkinitcpio-hook.sh $(DESTDIR)$(libdir)/kdump/kdump-mkinitcpio-git-hook.sh + install -D -m0644 initramfs/initcpio/kdump.hook $(DESTDIR)$(libdir)/kdump/initcpio/kdump.hook + install -D -m0644 initramfs/initcpio/kdump.install $(DESTDIR)$(libdir)/kdump/initcpio/kdump.install + +clean: + rm -f kdump-load.sh save-dumps.sh + rm -f initramfs/99-kdump-* + rm -f initramfs/dracut/{kdump-dracut-hook.sh,module-setup.sh} + rm -f initramfs/initcpio/kdump{-mkinitcpio-hook.sh,.hook,.install} diff --git a/initramfs/alpm-hook.generic b/initramfs/alpm-hook.generic new file mode 100644 index 0000000..e2a8216 --- /dev/null +++ b/initramfs/alpm-hook.generic @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# Copyright (c) 2022 Valve. +# Maintainer: Guilherme G. Piccoli +# +# alpm hook to deal with the kdump initramfs creation/deletion +# for INITRD users. This hook would act upon kernel installation, +# upgrade and removal, also on INITRD package installation. +# +[Trigger] +Type = Path +Operation = Install +Operation = Remove +Operation = Upgrade +Target = usr/lib/modules/*/vmlinuz + +[Trigger] +Type = Package +Operation = Install +Target = INITRD + +[Action] +Description = Managing kdump minimal initramfs and INITRD hooks... +Depends=INITRD +When = PostTransaction +Exec = /usr/lib/kdump/kdump-INITRD-hook.sh +NeedsTargets diff --git a/initramfs/alpm-script.header b/initramfs/alpm-script.header new file mode 100644 index 0000000..c68c337 --- /dev/null +++ b/initramfs/alpm-script.header @@ -0,0 +1,16 @@ +#!/bin/bash +set -eo pipefail # exclude -u on purpose to avoid issues with param checking +# +# SPDX-License-Identifier: LGPL-2.1+ +# +# Copyright (c) 2022 Valve. +# Maintainer: Guilherme G. Piccoli +# +# This is the generic alpm-hook script that handles kernel/package +# installation for INITRD users, on behalf of kdump. Covers both manual +# invocation (to create the initramfs image for a given kernel) as +# well as kernel package installation/removal trigger. It also handles +# the event of installing the package INITRD, which requires the +# installation of initramfs hooks to be executed early in boot time, +# in case of a kdump. +# diff --git a/initramfs/alpm-script.sh.in b/initramfs/alpm-script.sh.in new file mode 100644 index 0000000..b27af77 --- /dev/null +++ b/initramfs/alpm-script.sh.in @@ -0,0 +1,43 @@ +load_kdump_config + +# General comment of a caveat here: INITRD_installation() must be +# reentrant and only install the INITRD handlers if they're not +# already installed. This is necessary due to a kinda chicken-egg +# problem: the first time initramfs creation is attempted, we did't +# necessarily run the INITRD package installation hooks yet, hence +# we may be unable to properly create the initramfs image. "Easy" +# solution is to just try to install them always, it's cheap and not +# dependent of package install time magic. + +# Parameter passing case - since this is invoked by the kdump-load +# script, we must be sure that the INITRD package is available. +if [ -n "$1" ]; then + + if command -v INITRD 1>/dev/null; then + INITRD_installation + create_initramfs_INITRD "$1" + fi + exit 0 +fi + +while read -r line; do + # First case is the INITRD package installation. + if [[ "$line" != */vmlinuz ]]; then + INITRD_installation + exit 0 + fi + + # If reaching this point, we're installing/removing the kernel image. + VERSION="$(basename "$(dirname "$line")")" + + # If the file exists, means it's an installation step; + # notice that alpm suppresses the leading '/'. + if [ -f /"$line" ]; then + INITRD_installation + create_initramfs_INITRD "$VERSION" + else + rm -f "${MOUNT_FOLDER}"/kdump-initrd-"$VERSION".img + + fi + exit 0 +done diff --git a/initramfs/dracut/dracut-common.sh b/initramfs/dracut/dracut-common.sh new file mode 100644 index 0000000..72e78af --- /dev/null +++ b/initramfs/dracut/dracut-common.sh @@ -0,0 +1,34 @@ +# Functions to deal with dracut specifics, both for building +# the initramfs for dracut users, but also with regards to +# installing dracut specific hooks/scripts. +# +# IMPORTANT: it is assumed that kdump configuration was loaded +# before running any of these functions! +# +create_initramfs_dracut() { + rm -f "${MOUNT_FOLDER}/kdump-initrd-$1.img" + + DRACUT_NO_XATTR=1 dracut --no-early-microcode --host-only -q -m\ + "bash systemd systemd-initrd systemd-sysusers modsign dbus-daemon kdump dbus udev-rules dracut-systemd base fs-lib shutdown"\ + --kver "$1" "${MOUNT_FOLDER}/kdump-initrd-$1.img" + + if [ -s "${MOUNT_FOLDER}/kdump-initrd-$1.img" ]; then + logger "kdump: created dracut minimal initramfs" + fi +} + +dracut_installation() { + HOOKS_DIR="/usr/lib/kdump/dracut/" + + DRACUT_DIR="$(pkg-config --variable=dracutmodulesdir dracut 2>/dev/null)" + if [ -z "${DRACUT_DIR}" ]; then + DRACUT_DIR="/usr/lib/dracut/modules.d/" + fi + + if [ ! -d "${DRACUT_DIR}"/55kdump/ ]; then + install -D -m0755 "${HOOKS_DIR}"/kdump-collect.sh "${DRACUT_DIR}"/55kdump/kdump-collect.sh + install -D -m0755 "${HOOKS_DIR}"/module-setup.sh "${DRACUT_DIR}"/55kdump/module-setup.sh + logger "kdump: dracut hooks/scripts installed" + fi +} + diff --git a/module-setup.header b/initramfs/dracut/module-setup.header similarity index 100% rename from module-setup.header rename to initramfs/dracut/module-setup.header diff --git a/module-setup.sh.in b/initramfs/dracut/module-setup.sh.in similarity index 77% rename from module-setup.sh.in rename to initramfs/dracut/module-setup.sh.in index 3c7d32b..ed7c78f 100644 --- a/module-setup.sh.in +++ b/initramfs/dracut/module-setup.sh.in @@ -10,7 +10,7 @@ installkernel() { # to reduce the size of the minimal initramfs being created - kdump # is an non-graphical environment. This should have been already done # via dracut cmdline arguments, but play safe and delete here as well. - # Our list include the most common FWs/drivers (amd, i915, nvidia) + # Our list includes the most common FWs/drivers (amd, i915, nvidia). rm -rf "$initdir"/usr/lib/firmware/{amdgpu,i915,nvidia,radeon} rm -rf "$initdir"/usr/lib/modules/*/kernel/drivers/gpu/drm/{amd,i915,nouveau,radeon} @@ -20,12 +20,25 @@ installkernel() { exit 1 fi - hostonly='' instmods "${FSMOD}" + instmods "${FSMOD}" + + # We try here to be comprehensive and include the most common + # block modules to allow mounting the target device - notice + # that since we use hostonly, only the ones that make sense would + # get added, hence this list won't bloat the minimal image! + instmods aacraid + instmods ahci + instmods hpsa + instmods megaraid_sas + instmods mpt3sas + instmods nvme + instmods virtio_blk + instmods virtio-scsi } install() { # A valid makedumpfile is essential for the kdump initrd creation. - if [ ! -x "$(command -v makedumpfile)" ]; then + if ! command -v makedumpfile 1>/dev/null; then logger "kdump: failed to create dracut initrd, makedumpfile is missing" exit 1 fi diff --git a/initramfs/initcpio/initcpio-common.sh b/initramfs/initcpio/initcpio-common.sh new file mode 100644 index 0000000..20dac4d --- /dev/null +++ b/initramfs/initcpio/initcpio-common.sh @@ -0,0 +1,29 @@ +# Functions to deal with initcpio specifics, both for building +# the initramfs for mkinitcpio users, but also with regards to +# installing its specific hooks. +# +# IMPORTANT: it is assumed that kdump configuration was loaded +# before running any of these functions! +# +create_initramfs_mkinitcpio() { + rm -f "${MOUNT_FOLDER}/kdump-initrd-$1.img" + + mkinitcpio -A kdump -g "${MOUNT_FOLDER}/kdump-initrd-$1.img" "$1" 1>/dev/null + + if [ -s "${MOUNT_FOLDER}/kdump-initrd-$1.img" ]; then + logger "kdump: created initcpio minimal initramfs" + fi +} + +mkinitcpio_installation() { + KDUMP_HOOKS_DIR="/usr/lib/kdump/initcpio/" + INITCPIO_HOOKS="/usr/lib/initcpio/hooks" + INITCPIO_INST="/usr/lib/initcpio/install" + + if [ ! -e "${INITCPIO_HOOKS}"/kdump ] || [ ! -e "${INITCPIO_INST}"/kdump ]; then + install -D -m0644 "${KDUMP_HOOKS_DIR}"/kdump.hook "${INITCPIO_HOOKS}"/kdump + install -D -m0644 "${KDUMP_HOOKS_DIR}"/kdump.install "${INITCPIO_INST}"/kdump + logger "kdump: initcpio hooks installed" + fi +} + diff --git a/initramfs/initcpio/kdump.install.header b/initramfs/initcpio/kdump.install.header new file mode 100644 index 0000000..983d8a7 --- /dev/null +++ b/initramfs/initcpio/kdump.install.header @@ -0,0 +1,9 @@ +#!/bin/bash +# SPDX-License-Identifier: LGPL-2.1+ +# +# Copyright (c) 2022 Valve. +# Maintainer: Guilherme G. Piccoli +# +# Kdump-initrd module construction/inclusion script for +# initcpio-based initramfs. +# diff --git a/initramfs/initcpio/kdump.install.in b/initramfs/initcpio/kdump.install.in new file mode 100644 index 0000000..7fcb181 --- /dev/null +++ b/initramfs/initcpio/kdump.install.in @@ -0,0 +1,51 @@ +build() { + load_kdump_config + + # A valid makedumpfile is essential for the kdump initrd creation. + if ! command -v makedumpfile 1>/dev/null; then + logger "kdump: failed to create minimal initrd, makedumpfile is missing" + exit 1 + fi + + add_binary /usr/bin/date + add_binary /usr/bin/sync + add_binary "$(command -v makedumpfile)" + + # Copying kdump config/lib files is essential for a functional kdump. + add_full_dir /usr/share/kdump.d/ + add_full_dir /usr/lib/kdump/ + + # We need to derive the proper place to save the dump from the + # config files, in a way that makes possible to mount it in early boot. + DEVNODE="$(findmnt -n -o SOURCE --target "${MOUNT_FOLDER}")" + if [ -z "${DEVNODE}" ]; then + logger "kdump: error on devnode discovery" + exit 1 + fi + echo "${DEVNODE}" > "${BUILDROOT}"/usr/lib/kdump/kdump.mnt + + TGT="$(findmnt -n -o TARGET --target "${MOUNT_FOLDER}")" + if [ -z "${TGT}" ]; then + logger "kdump: error on base folder discovery" + exit 1 + fi + + BASE_FLD="${MOUNT_FOLDER#*$TGT}" + echo "${BASE_FLD}" > "${BUILDROOT}"/usr/lib/kdump/kdump.dir + + # Finally, we shouldn't have DRM/GPU drivers and firmwares here, + # but...just in case, let's remove all of that nevertheless. + # Our list includes the most common FWs/drivers (amd, i915, nvidia). + rm -rf "${BUILDROOT}"/usr/lib/firmware/{amdgpu,i915,nvidia,radeon} + rm -rf "${BUILDROOT}"/usr/lib/modules/*/kernel/drivers/gpu/drm/{amd,i915,nouveau,radeon} + + add_runscript +} + +help() { + cat < +# +# Script for effectively collecting the core dump/dmesg from +# within a minimal initrd - part of the kdump/pstore tooling. +# The most fail-prone operations are guarded with conditionals to +# bail in case we indeed fail - worst thing here would be to have +# a bad condition and get stuck in this minimal initrd with no +# output for the user. Notice that the script is used in both +# dracut and initcpio cases. +# + +#ENTRY POINT + # Bail out in case we don't have a vmcore, i.e. either we're not kdumping + # or something is pretty wrong and we wouldn't be able to progress. + VMCORE="/proc/vmcore" + if [ ! -f "$VMCORE" ]; then + reboot -f + fi + + # We have a more controlled situation with regards the config + # files here, since we manually added them in the initrd and + # the validation also happened there, during such addition, + # hence not requiring checks here. + for cfg in "/usr/share/kdump.d"/*; do + . "$cfg" + done + + KDUMP_TIMESTAMP=$(date -u +"%Y%m%d%H%M") + MOUNT_POINT="$(cat /usr/lib/kdump/kdump.mnt)" + BASE_FOLDER="$(cat /usr/lib/kdump/kdump.dir)" + KDUMP_FOLDER="/kdump_path/${BASE_FOLDER}/crash/${KDUMP_TIMESTAMP}" + + mkdir -p "/kdump_path" + if ! mount "${MOUNT_POINT}" /kdump_path; then + reboot -f + fi + + mkdir -p "${KDUMP_FOLDER}" + + # we want to split on spaces, it's a set of parameters! + # shellcheck disable=SC2086 + /usr/bin/makedumpfile ${MAKEDUMPFILE_DMESG_CMD} $VMCORE "${KDUMP_FOLDER}/dmesg.txt" + sync "${KDUMP_FOLDER}/dmesg.txt" + + if [ "${FULL_COREDUMP}" -ne 0 ]; then + # shellcheck disable=SC2086 + /usr/bin/makedumpfile ${MAKEDUMPFILE_COREDUMP_CMD} $VMCORE "${KDUMP_FOLDER}/vmcore.compressed" + sync "${KDUMP_FOLDER}/vmcore.compressed" + fi + + umount "${MOUNT_POINT}" + sync + + reboot -f +#END + diff --git a/kdump-collect.sh b/kdump-collect.sh deleted file mode 100644 index e4e5247..0000000 --- a/kdump-collect.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh -# -# SPDX-License-Identifier: LGPL-2.1+ -# -# Copyright (c) 2021 Valve. -# Maintainer: Guilherme G. Piccoli -# -# Script for effectively collecting the core dump/dmesg from -# within a minimal initrd - part of kdump/pstore tooling. -# The most fail-prone operations are guarded with conditionals to -# bail in case we indeed fail - worst thing here would be to have -# a bad condition and get stuck in this minimal initrd with no -# output for the user. -# - -# Bail out in case we don't have a vmcore, i.e. either we're not kdumping -# or something is pretty wrong and we wouldn't be able to progress. -VMCORE="/proc/vmcore" -if [ ! -f $VMCORE ]; then - reboot -f -fi - -# We have a more controlled situation with regards the config -# files here, since we manually added them in the initrd and -# the validation also happened there, during such addition, -# hence not requiring checks here. -for cfg in "/usr/share/kdump.d"/*; do - . "$cfg" -done - -KDUMP_TIMESTAMP=$(date -u +"%Y%m%d%H%M") -MOUNT_POINT="$(cat /usr/lib/kdump/kdump.mnt)" -BASE_FOLDER="$(cat /usr/lib/kdump/kdump.dir)" -KDUMP_FOLDER="/kdump_path/${BASE_FOLDER}/crash/${KDUMP_TIMESTAMP}" - -mkdir -p "/kdump_path" -if ! mount "${MOUNT_POINT}" /kdump_path; then - reboot -f -fi - -mkdir -p "${KDUMP_FOLDER}" - -# we want to split on spaces, it's a set of parameters! -# shellcheck disable=SC2086 -/usr/bin/makedumpfile ${MAKEDUMPFILE_DMESG_CMD} $VMCORE "${KDUMP_FOLDER}/dmesg.txt" -sync "${KDUMP_FOLDER}/dmesg.txt" - -if [ "${FULL_COREDUMP}" -ne 0 ]; then - # shellcheck disable=SC2086 - /usr/bin/makedumpfile ${MAKEDUMPFILE_COREDUMP_CMD} $VMCORE "${KDUMP_FOLDER}/vmcore.compressed" - sync "${KDUMP_FOLDER}/vmcore.compressed" -fi - -umount "${MOUNT_POINT}" -sync - -reboot -f diff --git a/kdump-load.sh.in b/kdump-load.sh.in index 656cd39..b61df85 100644 --- a/kdump-load.sh.in +++ b/kdump-load.sh.in @@ -41,17 +41,15 @@ grub_update() { # This function is responsible for creating the kdump initrd, either # via command-line call or in case initrd doesn't exist during kdump load. +# It accounts for both mkinitcpio and dracut users. create_initrd() { - rm -f "${MOUNT_FOLDER}/kdump-initrd-$(uname -r).img" - - echo "Creating the kdump initramfs for kernel \"$(uname -r)\" ..." - DRACUT_NO_XATTR=1 dracut --no-early-microcode --host-only -q -m\ - "bash systemd systemd-initrd systemd-sysusers modsign dbus-daemon kdump dbus udev-rules dracut-systemd base fs-lib shutdown"\ - --kver "$(uname -r)" "${MOUNT_FOLDER}/kdump-initrd-$(uname -r).img" + /usr/lib/kdump/kdump-mkinitcpio-hook.sh "$(uname -r)" + /usr/lib/kdump/kdump-dracut-hook.sh "$(uname -r)" } # This routine performs a clean-up by deleting the old/useless remaining -# kdump initrd files. +# kdump initrd files. Even with alpm-hooks, users might install kernels +# manually so it makes sense to have this fallback to avoid storage waste. cleanup_unused_initrd() { INSTALLED_KERNELS="${MOUNT_FOLDER}/.installed_kernels"