kexec-tools/mkdumprd
Tao Liu bf4667b866 bash scripts: fix variable quoting issue
upstream: fedora
resolves: bz2003832
conflict:
    function remove_kdump_kernel_key() not presented in rhel9,
    so related patch hunk are removed.

commit 86538ca6e2
Author: Kairui Song <kasong@redhat.com>
Date:   Wed Sep 8 17:21:41 2021 +0800

    bash scripts: fix variable quoting issue

    Fixed quoting issues found by shellcheck, no feature
    change. This should fix many errors when there is space
    in any shell variables, eg. dump target's name/path/id.

    False positives are marked with "# shellcheck disable=SCXXXX", for
    example, args are expected to split so it should not be quoted.

    And replaced some `cut -d ' ' -fX` with `awk '{print $X}'` since cut
    is fragile, and doesn't work well with any quoted strings that have
    redundant space.

    Following quoting related issues are fixed (check the link
    for example code and what could go wrong):

    https://github.com/koalaman/shellcheck/wiki/SC2046
    https://github.com/koalaman/shellcheck/wiki/SC2053
    https://github.com/koalaman/shellcheck/wiki/SC2068
    https://github.com/koalaman/shellcheck/wiki/SC2086
    https://github.com/koalaman/shellcheck/wiki/SC2206

    Signed-off-by: Kairui Song <kasong@redhat.com>
    Acked-by: Philipp Rudo <prudo@redhat.com>

Signed-off-by: Tao Liu <ltao@redhat.com>
2021-11-09 21:27:55 +08:00

465 lines
13 KiB
Bash

#!/bin/bash --norc
# New mkdumprd
#
# Copyright 2011 Red Hat, Inc.
#
# Written by Cong Wang <amwang@redhat.com>
#
if [[ -f /etc/sysconfig/kdump ]]; then
. /etc/sysconfig/kdump
fi
[[ $dracutbasedir ]] || dracutbasedir=/usr/lib/dracut
. $dracutbasedir/dracut-functions.sh
. /lib/kdump/kdump-lib.sh
. /lib/kdump/kdump-logger.sh
export IN_KDUMP=1
#initiate the kdump logger
dlog_init
if [[ $? -ne 0 ]]; then
echo "failed to initiate the kdump logger."
exit 1
fi
SSH_KEY_LOCATION="/root/.ssh/kdump_id_rsa"
SAVE_PATH=$(get_save_path)
OVERRIDE_RESETTABLE=0
extra_modules=""
dracut_args=( --add kdumpbase --quiet --hostonly --hostonly-cmdline --hostonly-i18n --hostonly-mode strict -o "plymouth dash resume ifcfg earlykdump" )
readonly MKDUMPRD_TMPDIR="$(mktemp -d -t mkdumprd.XXXXXX)"
[ -d "$MKDUMPRD_TMPDIR" ] || perror_exit "dracut: mktemp -p -d -t dracut.XXXXXX failed."
readonly MKDUMPRD_TMPMNT="$MKDUMPRD_TMPDIR/target"
trap '
ret=$?;
is_mounted $MKDUMPRD_TMPMNT && umount -f $MKDUMPRD_TMPMNT;
[[ -d $MKDUMPRD_TMPDIR ]] && rm --one-file-system -rf -- "$MKDUMPRD_TMPDIR";
exit $ret;
' EXIT
# clean up after ourselves no matter how we die.
trap 'exit 1;' SIGINT
add_dracut_arg() {
dracut_args+=( "$@" )
}
add_dracut_mount() {
add_dracut_arg "--mount" "$1"
}
add_dracut_sshkey() {
add_dracut_arg "--sshkey" "$1"
}
# caller should ensure $1 is valid and mounted in 1st kernel
to_mount() {
local _target=$1 _fstype=$2 _options=$3 _sed_cmd _new_mntpoint _pdev
_new_mntpoint=$(get_kdump_mntpoint_from_target "$_target")
_fstype="${_fstype:-$(get_fs_type_from_target "$_target")}"
_options="${_options:-$(get_mntopt_from_target "$_target")}"
_options="${_options:-defaults}"
if [[ "$_fstype" == "nfs"* ]]; then
_pdev=$_target
_sed_cmd+='s/,addr=[^,]*//;'
_sed_cmd+='s/,proto=[^,]*//;'
_sed_cmd+='s/,clientaddr=[^,]*//;'
else
# for non-nfs _target converting to use udev persistent name
_pdev="$(kdump_get_persistent_dev "$_target")"
if [[ -z $_pdev ]]; then
return 1
fi
fi
# mount fs target as rw in 2nd kernel
_sed_cmd+='s/\(^\|,\)ro\($\|,\)/\1rw\2/g;'
# with 'noauto' in fstab nfs and non-root disk mount will fail in 2nd
# kernel, filter it out here.
_sed_cmd+='s/\(^\|,\)noauto\($\|,\)/\1/g;'
# drop nofail or nobootwait
_sed_cmd+='s/\(^\|,\)nofail\($\|,\)/\1/g;'
_sed_cmd+='s/\(^\|,\)nobootwait\($\|,\)/\1/g;'
_options=$(echo "$_options" | sed "$_sed_cmd")
echo "$_pdev $_new_mntpoint $_fstype $_options"
}
#Function: get_ssh_size
#$1=dump target
#called from while loop and shouldn't read from stdin, so we're using "ssh -n"
get_ssh_size() {
local _out
local _opt=("-i" "$SSH_KEY_LOCATION" "-o" "BatchMode=yes" "-o" "StrictHostKeyChecking=yes")
if ! _out=$(ssh -q -n "${_opt[@]}" "$1" "df" "--output=avail" "$SAVE_PATH"); then
perror_exit "checking remote ssh server available size failed."
fi
echo -n "$_out" | tail -1
}
#mkdir if save path does not exist on ssh dump target
#$1=ssh dump target
#caller should ensure write permission on $1:$SAVE_PATH
#called from while loop and shouldn't read from stdin, so we're using "ssh -n"
mkdir_save_path_ssh()
{
local _opt _dir
_opt=(-i "$SSH_KEY_LOCATION" -o BatchMode=yes -o StrictHostKeyChecking=yes)
ssh -qn "${_opt[@]}" "$1" mkdir -p "$SAVE_PATH" 2>&1 > /dev/null
_ret=$?
if [[ $_ret -ne 0 ]]; then
perror_exit "mkdir failed on $1:$SAVE_PATH"
fi
#check whether user has write permission on $1:$SAVE_PATH
_dir=$(ssh -qn "${_opt[@]}" "$1" mktemp -dqp "$SAVE_PATH" 2>/dev/null)
_ret=$?
if [[ $_ret -ne 0 ]]; then
perror_exit "Could not create temporary directory on $1:$SAVE_PATH. Make sure user has write permission on destination"
fi
ssh -qn "${_opt[@]}" "$1" rmdir "$_dir"
return 0
}
#Function: get_fs_size
#$1=dump target
get_fs_size() {
df --output=avail "$(get_mntpoint_from_target "$1")/$SAVE_PATH" | tail -1
}
#Function: get_raw_size
#$1=dump target
get_raw_size() {
fdisk -s "$1"
}
#Function: check_size
#$1: dump type string ('raw', 'fs', 'ssh')
#$2: dump target
check_size() {
local avail memtotal
memtotal=$(awk '/MemTotal/{print $2}' /proc/meminfo)
case "$1" in
raw)
avail=$(get_raw_size "$2")
;;
ssh)
avail=$(get_ssh_size "$2")
;;
fs)
avail=$(get_fs_size "$2")
;;
*)
return
esac
if [[ $? -ne 0 ]]; then
perror_exit "Check dump target size failed"
fi
if [[ "$avail" -lt "$memtotal" ]]; then
dwarn "Warning: There might not be enough space to save a vmcore."
dwarn " The size of $2 should be greater than $memtotal kilo bytes."
fi
}
check_save_path_fs()
{
local _path=$1
if [[ ! -d $_path ]]; then
perror_exit "Dump path $_path does not exist."
fi
}
mount_failure()
{
local _target=$1
local _mnt=$2
local _fstype=$3
local msg="Failed to mount $_target"
if [[ -n "$_mnt" ]]; then
msg="$msg on $_mnt"
fi
msg="$msg for kdump preflight check."
if [[ $_fstype = "nfs" ]]; then
msg="$msg Please make sure nfs-utils has been installed."
fi
perror_exit "$msg"
}
check_user_configured_target()
{
local _target=$1 _cfg_fs_type=$2 _mounted
local _mnt=$(get_mntpoint_from_target "$_target")
local _opt=$(get_mntopt_from_target "$_target")
local _fstype=$(get_fs_type_from_target "$_target")
if [[ -n "$_fstype" ]]; then
# In case of nfs4, nfs should be used instead, nfs* options is deprecated in kdump.conf
[[ $_fstype = "nfs"* ]] && _fstype=nfs
if [[ -n "$_cfg_fs_type" ]] && [[ "$_fstype" != "$_cfg_fs_type" ]]; then
perror_exit "\"$_target\" have a wrong type config \"$_cfg_fs_type\", expected \"$_fstype\""
fi
else
_fstype="$_cfg_fs_type"
_fstype="$_cfg_fs_type"
fi
# For noauto mount, mount it inplace with default value.
# Else use the temporary target directory
if [[ -n "$_mnt" ]]; then
if ! is_mounted "$_mnt"; then
if [[ $_opt = *",noauto"* ]]; then
mount "$_mnt"
[[ $? -ne 0 ]] && mount_failure "$_target" "$_mnt" "$_fstype"
_mounted=$_mnt
else
perror_exit "Dump target \"$_target\" is neither mounted nor configured as \"noauto\""
fi
fi
else
_mnt=$MKDUMPRD_TMPMNT
mkdir -p "$_mnt"
mount "$_target" "$_mnt" -t "$_fstype" -o defaults
[[ $? -ne 0 ]] && mount_failure "$_target" "" "$_fstype"
_mounted=$_mnt
fi
# For user configured target, use $SAVE_PATH as the dump path within the target
if [[ ! -d "$_mnt/$SAVE_PATH" ]]; then
perror_exit "Dump path \"$_mnt/$SAVE_PATH\" does not exist in dump target \"$_target\""
fi
check_size fs "$_target"
# Unmount it early, if function is interrupted and didn't reach here, the shell trap will clear it up anyway
if [[ -n "$_mounted" ]]; then
umount -f -- "$_mounted"
fi
}
# $1: core_collector config value
verify_core_collector() {
local _cmd="${1%% *}"
local _params="${1#* }"
if [[ "$_cmd" != "makedumpfile" ]]; then
if is_raw_dump_target; then
dwarn "Warning: specifying a non-makedumpfile core collector, you will have to recover the vmcore manually."
fi
return
fi
if is_ssh_dump_target || is_raw_dump_target; then
if ! strstr "$_params" "-F"; then
perror_exit "The specified dump target needs makedumpfile \"-F\" option."
fi
_params="$_params vmcore"
else
_params="$_params vmcore dumpfile"
fi
# shellcheck disable=SC2086
if ! $_cmd --check-params $_params; then
perror_exit "makedumpfile parameter check failed."
fi
}
add_mount() {
local _mnt=$(to_mount "$@")
if [[ $? -ne 0 ]]; then
exit 1
fi
add_dracut_mount "$_mnt"
}
#handle the case user does not specify the dump target explicitly
handle_default_dump_target()
{
local _target
local _mntpoint
is_user_configured_dump_target && return
check_save_path_fs "$SAVE_PATH"
_save_path=$(get_bind_mount_source "$SAVE_PATH")
_target=$(get_target_from_path "$_save_path")
_mntpoint=$(get_mntpoint_from_target "$_target")
SAVE_PATH=${_save_path##"$_mntpoint"}
add_mount "$_target"
check_size fs "$_target"
}
# $1: function name
for_each_block_target()
{
local dev majmin
for dev in $(get_kdump_targets); do
[[ -b "$dev" ]] || continue
majmin=$(get_maj_min "$dev")
check_block_and_slaves "$1" "$majmin" && return 1
done
return 0
}
#judge if a specific device with $1 is unresettable
#return false if unresettable.
is_unresettable()
{
local path="/sys/$(udevadm info --query=all --path="/sys/dev/block/$1" | awk '/^P:/ {print $2}' | sed -e 's/\(cciss[0-9]\+\/\).*/\1/g' -e 's/\/block\/.*$//')/resettable"
local resettable=1
if [[ -f "$path" ]]
then
resettable="$(<"$path")"
[[ $resettable -eq 0 ]] && [[ "$OVERRIDE_RESETTABLE" -eq 0 ]] && {
local device=$(udevadm info --query=all --path="/sys/dev/block/$1" | awk -F= '/DEVNAME/{print $2}')
derror "Error: Can not save vmcore because device $device is unresettable"
return 0
}
fi
return 1
}
#check if machine is resettable.
#return true if resettable
check_resettable()
{
local _ret _target _override_resettable
_override_resettable=$(kdump_get_conf_val override_resettable)
OVERRIDE_RESETTABLE=${_override_resettable:-$OVERRIDE_RESETTABLE}
if [ "$OVERRIDE_RESETTABLE" != "0" ] && [ "$OVERRIDE_RESETTABLE" != "1" ];then
perror_exit "override_resettable value '$OVERRIDE_RESETTABLE' is invalid"
fi
for_each_block_target is_unresettable
_ret=$?
[[ $_ret -eq 0 ]] && return
return 1
}
check_crypt()
{
local _dev
for _dev in $(get_kdump_targets); do
if [[ -n $(get_luks_crypt_dev "$(get_maj_min "$_dev")") ]]; then
derror "Device $_dev is encrypted." && return 1
fi
done
}
if ! check_resettable; then
exit 1
fi
if ! check_crypt; then
dwarn "Warning: Encrypted device is in dump path, which is not recommended, see kexec-kdump-howto.txt for more details."
fi
# firstly get right SSH_KEY_LOCATION
keyfile=$(kdump_get_conf_val sshkey)
if [[ -f "$keyfile" ]]; then
# canonicalize the path
SSH_KEY_LOCATION=$(/usr/bin/readlink -m "$keyfile")
fi
while read -r config_opt config_val;
do
# remove inline comments after the end of a directive.
case "$config_opt" in
extra_modules)
extra_modules="$extra_modules $config_val"
;;
ext[234]|xfs|btrfs|minix|nfs)
check_user_configured_target "$config_val" "$config_opt"
add_mount "$config_val" "$config_opt"
;;
raw)
# checking raw disk writable
dd if="$config_val" count=1 of=/dev/null > /dev/null 2>&1 || {
perror_exit "Bad raw disk $config_val"
}
_praw=$(persistent_policy="by-id" kdump_get_persistent_dev "$config_val")
if [[ -z $_praw ]]; then
exit 1
fi
add_dracut_arg "--device" "$_praw"
check_size raw "$config_val"
;;
ssh)
if strstr "$config_val" "@";
then
mkdir_save_path_ssh "$config_val"
check_size ssh "$config_val"
add_dracut_sshkey "$SSH_KEY_LOCATION"
else
perror_exit "Bad ssh dump target $config_val"
fi
;;
core_collector)
verify_core_collector "$config_val"
;;
dracut_args)
while read -r dracut_arg; do
add_dracut_arg "$dracut_arg"
done <<< "$(echo "$config_val" | xargs -n 1 echo)"
;;
*)
;;
esac
done <<< "$(kdump_read_conf)"
handle_default_dump_target
if [[ -n "$extra_modules" ]]
then
add_dracut_arg "--add-drivers" "$extra_modules"
fi
# TODO: The below check is not needed anymore with the introduction of
# 'zz-fadumpinit' module, that isolates fadump's capture kernel initrd,
# but still sysroot.mount unit gets generated based on 'root=' kernel
# parameter available in fadump case. So, find a way to fix that first
# before removing this check.
if ! is_fadump_capable; then
# The 2nd rootfs mount stays behind the normal dump target mount,
# so it doesn't affect the logic of check_dump_fs_modified().
is_dump_to_rootfs && add_mount "$(to_dev_name "$(get_root_fs_device)")"
add_dracut_arg "--no-hostonly-default-device"
fi
dracut "${dracut_args[@]}" "$@"
_rc=$?
sync
exit $_rc