4b850d2f3a
Some Smart Array (hpsa/cciss) adapters don't support reset, we need to disable kdump on those devices, like rhel6 did. In this patch, the dump target is checked according to below criteria if it's a block device. If it's cciss disk but is resettbale, can be used as dump target. If it's cciss disk but is not resettable, can not be used as dump target. If it's cciss disk and not resettable, but user set OVERRIDE_RESETTABLE to 1 in /etc/sysconfig/kdump, can be taken as dump target. Because user know the situation and want to have a try. In this patch, added codes include 4 parts: 1)Add an option "override_resettable <0 | 1>" into kdump.conf, and add related section into kdump.conf man page. In mkdumprd, will check whether user has set a value, get that value if yes. By default, the value is 0. 2)port utility functions from dracut-functions.sh. 3)The check_resettable function checks if dump target is a resettable block device. This includes the case where default action dump_to_rootfs is set. Signed-off-by: Baoquan He <bhe@redhat.com> Acked-by: Vivek Goyal <vgoyal@redhat.com>
482 lines
12 KiB
Bash
482 lines
12 KiB
Bash
#!/bin/bash --norc
|
|
# New mkdumprd
|
|
#
|
|
# Copyright 2011 Red Hat, Inc.
|
|
#
|
|
# Written by Cong Wang <amwang@redhat.com>
|
|
#
|
|
|
|
export IN_KDUMP=1
|
|
|
|
conf_file="/etc/kdump.conf"
|
|
SSH_KEY_LOCATION="/root/.ssh/kdump_id_rsa"
|
|
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
|
|
exit 1
|
|
}
|
|
|
|
perror() {
|
|
echo $@ >&2
|
|
}
|
|
|
|
get_persistent_dev() {
|
|
local i _tmp _dev
|
|
|
|
_dev=$(udevadm info --query=name --name="$1" 2>/dev/null)
|
|
[ -z "$_dev" ] && {
|
|
perror_exit "Kernel dev name of $1 is not found."
|
|
}
|
|
|
|
for i in /dev/mapper/* /dev/disk/by-uuid/* /dev/disk/by-id/*; do
|
|
_tmp=$(udevadm info --query=name --name="$i" 2>/dev/null)
|
|
if [ "$_tmp" = "$_dev" ]; then
|
|
echo $i
|
|
return
|
|
fi
|
|
done
|
|
|
|
perror_exit "Persistent device name of $1 is not found."
|
|
}
|
|
|
|
add_dracut_arg() {
|
|
while [ $# -gt 0 ];
|
|
do
|
|
dracut_args+=("$1")
|
|
shift
|
|
done
|
|
}
|
|
|
|
add_dracut_module() {
|
|
add_dracut_arg "--add" "$1"
|
|
}
|
|
|
|
add_dracut_mount() {
|
|
add_dracut_arg "--mount" "$1"
|
|
}
|
|
|
|
add_dracut_sshkey() {
|
|
add_dracut_arg "--sshkey" "$1"
|
|
}
|
|
|
|
# Generic substring function. If $2 is in $1, return 0.
|
|
strstr() { [[ $1 =~ $2 ]]; }
|
|
|
|
target_is_root() {
|
|
local _t
|
|
_t=$(findmnt -k -n -r -o TARGET $1|sort|head -1)
|
|
[ "$_t" = "/" ]
|
|
}
|
|
|
|
# caller should ensure $1 is valid and mounted in 1st kernel
|
|
to_mount() {
|
|
local _dev=$1 _s _t _o _mntopts _pdev
|
|
|
|
_s=$(findmnt -k -f -n -r -o SOURCE $_dev)
|
|
_t=$(findmnt -k -f -n -r -o TARGET,FSTYPE $_dev)
|
|
_o=$(findmnt -k -f -n -r -o OPTIONS $_dev)
|
|
_o=${_o/#ro/rw} #mount fs target as rw in 2nd kernel
|
|
_mntopts="$_t $_o"
|
|
#for non-nfs _dev converting to use udev persistent name
|
|
if [ -b "$_s" ]; then
|
|
_pdev="$(get_persistent_dev $_s)"
|
|
else
|
|
_pdev=$_dev
|
|
fi
|
|
|
|
echo "$_pdev $_mntopts"
|
|
}
|
|
|
|
to_mount_point() {
|
|
echo $(findmnt -k -f -n -r -o TARGET $1)
|
|
}
|
|
|
|
is_readonly_mount() {
|
|
local _mnt
|
|
_mnt=$(findmnt -k -f -n -r -o OPTIONS $1)
|
|
|
|
#fs/proc_namespace.c: show_mountinfo():
|
|
#seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
|
|
[[ "$_mnt" =~ ^ro ]]
|
|
}
|
|
|
|
#Function: get_ssh_size
|
|
#$1=dump target
|
|
get_ssh_size() {
|
|
local _opt _out _size
|
|
_opt="-i $SSH_KEY_LOCATION -o BatchMode=yes -o StrictHostKeyChecking=yes"
|
|
_out=$(ssh -q -n $_opt $1 "df -P $SAVE_PATH")
|
|
[ $? -ne 0 ] && {
|
|
perror_exit "checking remote ssh server available size failed."
|
|
}
|
|
#ssh output removed the line break, so print $11 instead of $4
|
|
_size=$(echo -n $_out|tail -1 | awk '{print $11}')
|
|
echo -n $_size
|
|
}
|
|
|
|
#mkdir if save path does not exist on dump target filesystem
|
|
#$1=dump target
|
|
#caller should ensure $1 is mounted
|
|
mkdir_save_path() {
|
|
local _mnt=$(to_mount_point $1)
|
|
local _remount="no"
|
|
local _ret
|
|
|
|
[ ! -d ${_mnt}/$SAVE_PATH ] && {
|
|
if is_readonly_mount $1; then
|
|
echo "Mounting $1 as read-write for creating dump directory.."
|
|
mount -o remount,rw $1 || {
|
|
perror_exit "Mounting $1 as read-write failed."
|
|
}
|
|
_remount="yes"
|
|
fi
|
|
mkdir -p ${_mnt}/$SAVE_PATH
|
|
_ret=$?
|
|
[ "$_remount" = "yes" ] && {
|
|
echo "Remounting $1 as read-only."
|
|
mount -o remount,ro $1 || {
|
|
perror_exit "Remounting $1 as read-only failed."
|
|
}
|
|
}
|
|
[ $_ret -ne 0 ] && {
|
|
perror_exit "Creating ${_mnt}/$SAVE_PATH failed."
|
|
}
|
|
}
|
|
}
|
|
|
|
#Function: get_fs_size
|
|
#$1=dump target
|
|
get_fs_size() {
|
|
local _mnt=$(to_mount_point $1)
|
|
echo -n $(df -P "${_mnt}/$SAVE_PATH"|tail -1|awk '{print $4}')
|
|
}
|
|
|
|
#Function: get_raw_size
|
|
#$1=dump target
|
|
get_raw_size() {
|
|
echo -n $(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 [ $avail -lt $memtotal ]; then
|
|
echo "Warning: There might not be enough space to save a vmcore."
|
|
echo " The size of $2 should be greater than $memtotal kilo bytes."
|
|
fi
|
|
}
|
|
|
|
is_nfs_dump_target()
|
|
{
|
|
grep -q "^nfs" $conf_file
|
|
}
|
|
|
|
is_ssh_dump_target()
|
|
{
|
|
grep -q "^ssh.*@" $conf_file
|
|
}
|
|
|
|
is_raw_dump_target()
|
|
{
|
|
grep -q "^raw" $conf_file
|
|
}
|
|
|
|
# $1: core_collector config value
|
|
verify_core_collector() {
|
|
if grep -q "^raw" $conf_file && [ "${1%% *}" != "makedumpfile" ]; then
|
|
echo "Warning: specifying a non-makedumpfile core collector, you will have to recover the vmcore manually."
|
|
fi
|
|
if is_ssh_dump_target || is_raw_dump_target; then
|
|
if [ "${1%% *}" = "makedumpfile" ]; then
|
|
! strstr "$1" "-F" && {
|
|
perror_exit "The specified dump target needs makedumpfile \"-F\" option."
|
|
}
|
|
fi
|
|
fi
|
|
}
|
|
|
|
add_mount() {
|
|
if ! target_is_root "$1"; then
|
|
local _mnt=$(to_mount "$1")
|
|
add_dracut_mount "$_mnt"
|
|
fi
|
|
}
|
|
|
|
# get_maj_min <device>
|
|
# 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
|
|
# canonicalize the path
|
|
SSH_KEY_LOCATION=$(/usr/bin/readlink -m $keyfile)
|
|
fi
|
|
|
|
if [ "$(uname -m)" = "s390x" ]; then
|
|
add_dracut_module "znet"
|
|
fi
|
|
|
|
while read config_opt config_val;
|
|
do
|
|
case "$config_opt" in
|
|
extra_modules)
|
|
extra_modules="$extra_modules $config_val"
|
|
;;
|
|
ext[234]|xfs|btrfs|minix|nfs)
|
|
if ! findmnt $config_val >/dev/null; then
|
|
perror_exit "Dump target $config_val is probably not mounted."
|
|
fi
|
|
|
|
if [ "$config_opt" = "nfs" ]; then
|
|
add_dracut_module "nfs"
|
|
fi
|
|
add_mount "$config_val"
|
|
mkdir_save_path $config_val
|
|
check_size fs $config_val
|
|
;;
|
|
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"
|
|
}
|
|
add_dracut_arg "--device" "$(get_persistent_dev $config_val)"
|
|
check_size raw $config_val
|
|
;;
|
|
ssh)
|
|
if strstr "$config_val" "@";
|
|
then
|
|
check_size ssh $config_val
|
|
add_dracut_module "ssh-client"
|
|
add_dracut_sshkey "$SSH_KEY_LOCATION"
|
|
else
|
|
perror_exit "Bad ssh dump target $config_val"
|
|
fi
|
|
;;
|
|
core_collector)
|
|
verify_core_collector "$config_val"
|
|
;;
|
|
*)
|
|
if [ -n $(echo $config_opt | grep "^#.*$") ]
|
|
then
|
|
continue
|
|
fi
|
|
;;
|
|
esac
|
|
done < $conf_file
|
|
|
|
if [ -n "$extra_modules" ]
|
|
then
|
|
add_dracut_arg "--add-drivers" "$extra_modules"
|
|
fi
|
|
|
|
dracut "${dracut_args[@]}" -M "$@"
|
|
_rc=$?
|
|
sync
|
|
exit $_rc
|