diff --git a/kdump.conf b/kdump.conf index afa2af8..de3ad4a 100644 --- a/kdump.conf +++ b/kdump.conf @@ -113,6 +113,12 @@ # - By default, kdump initrd only will be rebuilt when # necessary. Specify 1 to force rebuilding kdump # initrd every time when kdump service starts. +# +#override_resettable <0 | 1> +# - Usually a unresettable block device can't be dump target. +# Specifying 1 means though block target is unresettable, user +# understand this situation and want to try dumping. By default, +# it's set to 0, means not to try a destined failure. #raw /dev/vg/lv_kdump #ext4 /dev/vg/lv_kdump diff --git a/kdump.conf.5 b/kdump.conf.5 index 31761f8..9e498c5 100644 --- a/kdump.conf.5 +++ b/kdump.conf.5 @@ -163,6 +163,14 @@ By default, kdump initrd only will be rebuilt when necessary. Specify 1 to force rebuilding kdump initrd every time when kdump service starts. .RE +.B override_resettable <0 | 1> +.RS +Usually a unresettable block device can't be dump target. Specifying 1 means +though block target is unresettable, user understand this situation and want +to try dumping. By default, it's set to 0, means not to try a destined failure. +.RE + + .SH DEPRECATED OPTIONS .B net | diff --git a/mkdumprd b/mkdumprd index b925c09..c85fe2a 100644 --- a/mkdumprd +++ b/mkdumprd @@ -14,6 +14,7 @@ SAVE_PATH=$(grep ^path $conf_file| cut -d' ' -f2) [ -z "$SAVE_PATH" ] && SAVE_PATH="/var/crash" extra_modules="" dracut_args=("--hostonly" "-o" "plymouth dash") +OVERRIDE_RESETTABLE=0 perror_exit() { echo $@ >&2 @@ -188,6 +189,11 @@ check_size() { fi } +is_nfs_dump_target() +{ + grep -q "^nfs" $conf_file +} + is_ssh_dump_target() { grep -q "^ssh.*@" $conf_file @@ -219,6 +225,192 @@ add_mount() { fi } +# get_maj_min +# Prints the major and minor of a device node. +# Example: +# $ get_maj_min /dev/sda2 +# 8:2 +get_maj_min() { + local _dev + _dev=$(stat -L -c '$((0x%t)):$((0x%T))' "$1" 2>/dev/null) + _dev=$(eval "echo $_dev") + echo $_dev +} + +# ugly workaround for the lvm design +# There is no volume group device, +# so, there are no slave devices for volume groups. +# Logical volumes only have the slave devices they really live on, +# but you cannot create the logical volume without the volume group. +# And the volume group might be bigger than the devices the LV needs. +check_vol_slaves() { + local _lv _vg _pv + for i in /dev/mapper/*; do + _lv=$(get_maj_min $i) + if [[ $_lv = $2 ]]; then + _vg=$(lvm lvs --noheadings -o vg_name $i 2>/dev/null) + # strip space + _vg=$(echo $_vg) + if [[ $_vg ]]; then + for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2>/dev/null) + do + check_block_and_slaves $1 $(get_maj_min $_pv) && return 0 + done + fi + fi + done + return 1 +} + +# Walk all the slave relationships for a given block device. +# Stop when our helper function returns success +# $1 = function to call on every found block device +# $2 = block device in major:minor format +check_block_and_slaves() { + local _x + [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry. + "$1" $2 && return + check_vol_slaves "$@" && return 0 + if [[ -f /sys/dev/block/$2/../dev ]]; then + check_block_and_slaves $1 $(cat "/sys/dev/block/$2/../dev") && return 0 + fi + [[ -d /sys/dev/block/$2/slaves ]] || return 1 + for _x in /sys/dev/block/$2/slaves/*/dev; do + [[ -f $_x ]] || continue + check_block_and_slaves $1 $(cat "$_x") && return 0 + done + return 1 +} + +to_dev_name() { + local dev="${1//\"/}" + + case "$dev" in + UUID=*) + dev=`blkid -U "${dev#UUID=}"` + ;; + LABEL=*) + dev=`blkid -L "${dev#LABEL=}"` + ;; + esac + echo $dev +} + +get_block_dump_target() +{ + local _target + + if is_ssh_dump_target || is_nfs_dump_target; then + return + fi + + _target=$(egrep "^ext[234]|^xfs|^btrfs|^minix|^raw" /etc/kdump.conf 2>/dev/null |awk '{print $2}') + [ -n "$_target" ] && echo $(to_dev_name $_target) && return + + #get rootfs device name + _target=$(findmnt -k -f -n -o SOURCE /) + [ -b "$_target" ] && echo $(to_dev_name $_target) +} + +get_default_action_target() +{ + local _target + local _action=$(grep "^default" /etc/kdump.conf 2>/dev/null | awk '{print $2}') + if [ -n "$_action" ] && [ "$_action" = "dump_to_rootfs" ]; then + #get rootfs device name + _target=$(findmnt -k -f -n -o SOURCE /) + [ -b "$_target" ] && echo $(to_dev_name $_target) + fi + return +} + +get_override_resettable() +{ + local override_resettable + + override_resettable=$(grep "^override_resettable" $conf_file) + if [ -n "$override_resettable" ]; then + OVERRIDE_RESETTABLE=$(echo $override_resettable | cut -d' ' -f2) + if [ "$OVERRIDE_RESETTABLE" != "0" ] && [ "$OVERRIDE_RESETTABLE" != "1" ];then + perror_exit "override_resettable value $OVERRIDE_RESETTABLE is invalid" + fi + fi +} + + +# $1: function name +for_each_block_target() +{ + local dev majmin + + #check dump target + dev=$(get_block_dump_target) + + if [ -n "$dev" ]; then + majmin=$(get_maj_min $dev) + check_block_and_slaves $1 $majmin && return 1 + fi + + #check rootfs when default action dump_to_rootfs is set + dev=$(get_default_action_target) + if [ -n "$dev" ]; then + majmin=$(get_maj_min $dev) + check_block_and_slaves $1 $majmin && return 2 + fi + + 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="$(cat $path)" + [ $resettable -eq 0 -a "$OVERRIDE_RESETTABLE" -eq 0 ] && { + local device=$(udevadm info --query=all --path=/sys/dev/block/$1 | awk -F= '/DEVNAME/{print $2}') + echo "Device $device is unresettable" + return 0 + } + fi + + return 1 +} + +#check if machine is resettable. +#return true if resettable +check_resettable() +{ + local _ret _target + + get_override_resettable + + for_each_block_target is_unresettable + _ret=$? + + [ $_ret -eq 0 ] && return + + if [ $_ret -eq 1 ]; then + _target=$(get_block_dump_target) + perror "Can not save vmcore to target device $_target . This device can not be initialized in kdump kernel as it is not resettable" + elif [ $_ret -eq 2 ]; then + _target=$(get_default_action_target) + perror "Rootfs device $_target is not resettable, can not be used as the default target, please specify a default action" + fi + + return 1 +} + +if ! check_resettable; then + exit 1 +fi + # firstly get right SSH_KEY_LOCATION keyfile=$(awk '/^sshkey/ {print $2}' $conf_file) if [ -f "$keyfile" ]; then