Files
kdumpst/kdumpst-load.sh.in

234 lines
7.2 KiB
Bash

# This function has 2 purposes: if 'kdump' is passed as argument and we don't
# have crashkernel memory reserved, we edit grub config file and recreate
# grub.cfg, so next boot has it reserved; in this case, we also bail-out,
# since kdump can't be loaded anyway.
#
# If 'pstore' is passsed as argument, we try to unset crashkernel iff it's
# already set AND the pattern in grub config is the one added by us - if the
# users set crashkernel themselves, we don't mess with that.
grub_update() {
CRASHK="$(cat /sys/kernel/kexec_crash_size)"
SED_ADD="s/^GRUB_CMDLINE_LINUX_DEFAULT=\"/GRUB_CMDLINE_LINUX_DEFAULT=\"${GRUB_CMDLINE}/g"
if [ "${GRUB_AUTOSET}" -eq 1 ]; then
if [ "$1" = "kdump" ] && [ "${CRASHK}" -eq 0 ]; then
sed -i "${SED_ADD}" "${GRUB_CFG_FILE}"
if ! grub-mkconfig -o "${GRUB_BOOT_FILE}" 1>/dev/null; then
logger "kdumpst: failed to execute command \"${GRUB_CMD}\""
exit 1
fi
sync "${GRUB_BOOT_FILE}" 2>/dev/null
logger "kdumpst: kexec won't succeed, no reserved memory in this boot..."
logger "kdumpst: but we automatically set crashkernel for next boot."
exit 0 # this is considered a successful run
fi
if [ "$1" = "pstore" ] && [ "${CRASHK}" -ne 0 ]; then
# Let's be sure our cmdline is there...
if ! grep -q "${GRUB_CMDLINE}" "${GRUB_CFG_FILE}"; then
return 0
fi
sed -i "s/\"${GRUB_CMDLINE}/\"/g" "${GRUB_CFG_FILE}"
if ! grub-mkconfig -o "${GRUB_BOOT_FILE}" 1>/dev/null; then
logger "kdumpst: failed to execute command \"${GRUB_CMD}\""
exit 1
fi
sync "${GRUB_BOOT_FILE}" 2>/dev/null
logger "kdumpst: cleared crashkernel memory previously set."
fi
fi
}
# 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() {
/usr/lib/kdumpst/kdump-mkinitcpio-hook.sh "$(uname -r)"
/usr/lib/kdumpst/kdump-dracut-hook.sh "$(uname -r)"
}
# This routine performs a clean-up by deleting the old/useless remaining
# 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"
find /lib/modules/* -maxdepth 0 -type d -exec basename {} \; 1> "${INSTALLED_KERNELS}"
find "${MOUNT_FOLDER}"/* -name "kdump-initrd*" -type f -print0 2>/dev/null |\
while IFS= read -r -d '' file
do
FNAME="$(basename "${file}" .img)"
KVER="${FNAME#kdump-initrd-}"
if ! grep -q "${KVER}" "${INSTALLED_KERNELS}" ; then
rm -f "${MOUNT_FOLDER}/${FNAME}.img"
logger "kdumpst: removed unused file \"${FNAME}.img\""
fi
done
rm -f "${INSTALLED_KERNELS}"
}
# Now this routine performs a full deletion of all kdump initrd files.
clear_all_initrds() {
rm -f "${MOUNT_FOLDER}"/kdump-initrd-*
}
# Next routine parses /proc/iomem to obtain the biggest RAM buffer
# available. Saves the results in 2 global variables: MEM_{SIZE,START}.
parse_ram_buffers() {
BUFFERS="$(grep "$1" /proc/iomem)"
while read -r line
do
RANGE=$(echo "$line" | sed 's/^[[:space:]]*//g' | cut -f1 -d:)
if [ "${RANGE}" == '' ]; then
continue
fi
MEM_END=$(echo "$RANGE" | cut -f2 -d-)
MSTART=$(echo "$RANGE" | cut -f1 -d-)
MSIZE=$(( 16#${MEM_END} - 16#${MSTART} ))
if [ ${MSIZE} -gt "${MEM_SIZE}" ]; then
MEM_SIZE="${MSIZE}"
MEM_START="${MSTART}"
fi
done <<< "${BUFFERS}"
# Trick to preserve global variables modifications inside loops
# using here-strings got from https://stackoverflow.com/a/16854326 .
}
# Function to display basic help about how to use this tool.
usage() {
cat <<EOF
${0##*/} <COMMAND>
kdumpst loader.
Options:
load
Load pstore/kdump according to the configuration file.
create-initrd
Create the minimal kdump initrd for the running kernel.
clear-initrd
Delete all kdump minimal initrd images.
EOF
}
preamble() {
load_kdumpst_config
# In case the kdumpst main folder doesn't exist, create it
# here, as soon as possible.
mkdir -p "${MOUNT_FOLDER}"
}
# Entry point of the script.
case $1 in
clear-initrd)
preamble
clear_all_initrds
exit 0
;;
create-initrd)
preamble
create_initrd
exit 0
;;
load)
# just bail from the case statement, jumping to code below
;;
*)
usage
exit 1
;;
esac
# Here starts the main purpose of this script, the load operation.
preamble
# Pstore-RAM load; if it is configured via the config files and fails
# to configure pstore, we still try to load the kdump. We try to reserve
# here a ${MEM_REQUIRED} memory region.
# Notice that we assume ramoops is a module here - if built-in, users
# should properly load it through command-line parameters.
if [ "${USE_PSTORE_RAM}" -eq 1 ]; then
MEM_REQUIRED="${PSTORE_MEM_AMOUNT}"
RECORD_SIZE="${PSTORE_RECORD_SZ}"
MEM_SIZE=0
parse_ram_buffers "RAM buffer"
if [ "${MEM_SIZE}" -eq 0 ] && [ "${PSTORE_RAM_USE_CRASH_MEM}" -eq 1 ]; then
parse_ram_buffers "Crash kernel"
fi
if [ "${MEM_SIZE}" -gt 0 ]; then
if modprobe ramoops mem_address=0x"${MEM_START}" mem_size="${MEM_REQUIRED}" record_size="${RECORD_SIZE}"; then
# If Pstore is set, update grub.cfg to avoid reserving crashkernel memory.
logger "kdumpst: pstore-RAM was loaded successfully"
cleanup_unused_initrd
grub_update pstore
exit 0
fi
logger "kdumpst: pstore-RAM load failed...will try kdump"
fi
# Fallback to kdump load - if we fail when configuring pstore, better
# trying kdump; in case we have crashkernel memory reserved, lucky us.
# If not, we're going to set that automatically on grub_update().
# Notice that if it's not set, we bail-out in grub_update() - there's
# no point in continuing since kdump cannot work.
fi
cleanup_unused_initrd
grub_update kdump
# The kdump kernel command-line has some parameters appended, check
# the configuration files in order to change that. We also remove huge
# pages settings here (and crashkernel reservation), to save memory on kdump.
KDUMP_CMDLINE=$(sed -re 's/(^| )(crashkernel|hugepages|hugepagesz)=[^ ]*//g;s/"/\\\\"/' /proc/cmdline)
KDUMP_CMDLINE="${KDUMP_CMDLINE} ${KDUMP_APPEND_CMDLINE}"
# To obtain the vmlinux binary path, try first using directly the command-line
# information. If it fails, then attempt prepending /boot into that (faced
# both situations in the field so far).
VMLINUX="$(grep -o 'BOOT_IMAGE=[^ ]*' /proc/cmdline)"
VMLINUX="${VMLINUX#*BOOT_IMAGE=${GRUB_BOOT_IMAGE_PREFIX}}"
if [ ! -s "${VMLINUX}" ]; then
VMLINUX="/boot/${VMLINUX}"
if [ ! -s "${VMLINUX}" ]; then
logger "kdumpst: couldn't find the kernel image"
exit 1
fi
fi
# In case we don't have a valid initrd, for some reason, try creating
# one before loading kdump (or else it will fail).
INITRD_FNAME="${MOUNT_FOLDER}/kdump-initrd-$(uname -r).img"
if [ ! -s "${INITRD_FNAME}" ]; then
create_initrd
# Check if create_initrd() succeeded.
if [ ! -s "${INITRD_FNAME}" ]; then
logger "kdumpst: failure on initrd creation - aborting."
exit 1
fi
fi
if ! kexec -s -p "${VMLINUX}" --initrd "${INITRD_FNAME}" --append="${KDUMP_CMDLINE}"; then
logger "kdumpst: kexec load failed"
exit 1
fi
logger "kdumpst: panic kexec loaded successfully"
# Local Variables:
# indent-tabs-mode: t
# End: