kexec-tools/kdump

322 lines
7.4 KiB
Bash

#! /bin/sh
#
# Copyright 2005 Red Hat, Inc.
# Author: Jeff Moyer <jmoyer@redhat.com>
# Modifications for SUSE from Chris Mason <mason@suse.com>
# Takashi Iwai <tiwai@suse.de>
#
# kdump
#
# Description: The kdump init script provides the support necessary for
# loading a kdump kernel into memory at system bootup time,
# and for copying away a vmcore at system panic time.
#
#
# /etc/init.d/kexec
### BEGIN INIT INFO
# Provides: kdump
# Required-Start: boot.localfs $remote_fs
# Should-Start:
# Required-Stop:
# Default-Start: 1 2 3 5
# Default-Stop:
# Description: kdump core saving and boot configuration
### END INIT INFO
. /etc/sysconfig/kdump
. /etc/rc.status
KEXEC=/sbin/kexec
KDUMP_HELPER=/usr/sbin/kdump-helper
BOOTDIR="/boot"
# purge old dump directories if the number of directories
# exceeds $KDUMP_KEEP_OLD_DUMPS
purge_old_dumps()
{
dirs=`ls -d $KDUMP_SAVEDIR/*-*-*-*:* 2>/dev/null | sort`
numdirs=`echo $dirs | wc -w`
for d in $dirs; do
if [ $numdirs -le $KDUMP_KEEP_OLD_DUMPS ]; then
break;
fi
echo " Expiring old dump $d"
rm -rf $d
numdirs=`expr $numdirs - 1`
done
}
# get the free disk space of the given directory in MB
parse_rest_size()
{
test -d "$1" || mkdir -p "$1"
hdread=""
df -P "$1" | while read fs bl us av rest; do
test -n "$hdread" && expr $av / 1024
hdread="$fs"
done
}
# get vmcore size in MB
get_mem_size()
{
s=`stat -c '%s' /proc/vmcore`
expr \( $s + 1048575 \) / 1048576
}
# The default dumper
#
# Clean up old stuff if necessary, check the free size
# and save the vmcore
save_core()
{
if [ $KDUMP_KEEP_OLD_DUMPS -gt 0 ]; then
purge_old_dumps
fi
if [ $KDUMP_FREE_DISK_SIZE -gt 0 ]; then
restsize=`parse_rest_size "$KDUMP_SAVEDIR"`
needsize=`get_mem_size`
needsize=`expr $needsize + $KDUMP_FREE_DISK_SIZE`
if [ $restsize -lt $needsize ]; then
echo -n " No enough space left on dump device ($restsize MB)"
rc_status -s
rc_failed 6
return
fi
fi
coredir="${KDUMP_SAVEDIR}/`date +"%Y-%m-%d-%H:%M"`"
mkdir -p $coredir
echo -n "Saving crash dump to $coredir"
cp --sparse=always /proc/vmcore $coredir/vmcore
rc_status -v
}
# print the available kdump kernel path
# empty if no matching file is found
check_boot_kernel ()
{
local kstr
kstr="${BOOTDIR}/vmlinux-$1$2"
if [ -f $kstr ]; then
echo $kstr
return
fi
kstr="$kstr.gz"
if [ -f $kstr ]; then
echo $kstr
return
fi
case `uname -i` in
ia64)
# ia64 uses vmlinuz as of vmlinux.gz
kstr="${BOOTDIR}/vmlinuz-$1$2"
if [ -f $kstr ]; then
echo $kstr
return
fi
;;
esac
}
# Load the kdump kerel specified in /etc/sysconfig/kdump
# If none is specified, try to load a kdump kernel with the same version
# as the currently running kernel.
load_kdump()
{
echo -n "Loading kdump "
if [ -z "$KDUMP_KERNELVER" ]; then
kdump_kver=`uname -r | sed -e's/-[^-]*$//g'`
kdump_kernel=`check_boot_kernel $kdump_kver -kdump`
if [ -n "$kdump_kernel" ]; then
kdump_kver="${kdump_kver}-kdump"
elif [ -z "$kdump_kernel" ]; then
kdump_kver=`uname -r`
kdump_kernel=`check_boot_kernel $kdump_kver`
fi
else
kdump_kver="$KDUMP_KERNELVER"
kdump_kernel=`check_boot_kernel $kdump_kver`
fi
if [ -z "$kdump_kernel" -o ! -f "$kdump_kernel" ]; then
echo -n ": No kdump kernel image found for kernel $kdump_kver."
rc_status -s
rc_failed 6
rc_exit
fi
kdump_initrd="${BOOTDIR}/initrd-${kdump_kver}"
if [ ! -f $kdump_initrd ]; then
echo -n ": No kdump initial ramdisk found."
echo "Tried to locate ${kdump_initrd}"
rc_status -s
rc_failed 6
rc_exit
fi
if [ -z "$KDUMP_COMMANDLINE" ]; then
KDUMP_COMMANDLINE=`cat /proc/cmdline | \
sed -e 's/crashkernel=[0-9]\+[mM]\(@[0-9]\+[Mm]\?\)\?//g' \
-e 's/ *splash=[^ ]*/ /g' \
-e 's/ *BOOT_IMAGE=[^ ]* / /g' \
-e 's/ *showopts/ /g'`
# Use deadline for saving the memory footprint
KDUMP_COMMANDLINE="$KDUMP_COMMANDLINE elevator=deadline sysrq=1"
case `uname -i` in
i?86|x86_64|ia64)
KDUMP_COMMANDLINE="$KDUMP_COMMANDLINE irqpoll"
;;
esac
fi
KDUMP_COMMANDLINE="CRASH=1 $KDUMP_COMMANDLINE"
if [ -n "$KDUMP_COMMANDLINE_APPEND" ] ; then
KDUMP_COMMANDLINE="$KDUMP_COMMANDLINE $KDUMP_COMMANDLINE_APPEND"
fi
if [ -n "$KDUMP_RUNLEVEL" ]; then
case "$KDUMP_RUNLEVEL" in
[1-5s])
KDUMP_COMMANDLINE="$KDUMP_COMMANDLINE $KDUMP_RUNLEVEL"
;;
*)
echo " : Invalid KDUMP_RUNLEVEL=$KDUMP_RUNLEVEL, ignored"
;;
esac
fi
# add the dump device
if [ -n "$KDUMP_DUMPDEV" ] ; then
KDUMP_COMMANDLINE="dumpdev=$KDUMP_DUMPDEV $KDUMP_COMMANDLINE"
fi
echo 1 > /proc/sys/kernel/panic_on_oops
$KEXEC -p $kdump_kernel \
--append="$KDUMP_COMMANDLINE" \
--initrd=$kdump_initrd \
$KEXEC_OPTIONS
rc_status -v
}
# return success if running in a crash environemnt
is_crash_kernel ()
{
test -f /proc/vmcore || return 1
# FIXME: any better way to detect crash environment?
test -n "$CRASH" && return 0
case `uname -i` in
ia64)
# ia64 has no kdump kernel
return 1;;
esac
return 0
}
# return success if we have a valid dump on the dump device
have_valid_dump_in_dumpdev ()
{
if [ ! -b "$KDUMP_DUMPDEV" ] ; then
return 1
fi
# return the return code from this command
$KDUMP_HELPER -c "$KDUMP_DUMPDEV" >> /dev/null
}
# invalidate the dump device so that it's not read on next boot
invalidate_dumpdev ()
{
dd if=/dev/zero of=$KDUMP_DUMPDEV bs=512 count=1
}
# copy the dump from the dumpdevice to the harddisk
copy_dump_from_dumpdev ()
{
if [ $KDUMP_KEEP_OLD_DUMPS -gt 0 ]; then
purge_old_dumps
fi
dumpsize=`$KDUMP_HELPER -l "$KDUMP_DUMPDEV" | sed -e 's/Length: //g'`
if [ -z "$dumpsize" -o "$dumpsize" = 0 ] ; then
echo -n " Unable to retrieve the dump size"
rc_status -s
rc_failed
fi
dumpsize_mb=$(($dumpsize / 1024 / 1024))
if [ $KDUMP_FREE_DISK_SIZE -gt 0 ]; then
restsize=`parse_rest_size "$KDUMP_SAVEDIR"`
needsize=`expr $dumpsize_mb + $KDUMP_FREE_DISK_SIZE`
if [ $restsize -lt $needsize ]; then
echo -n " No enough space left on dump device ($restsize MB)"
rc_status -s
rc_failed 6
return
fi
fi
coredir="${KDUMP_SAVEDIR}/`date +"%Y-%m-%d-%H:%M"`"
mkdir -p $coredir
echo -n "Saving crash dump to $coredir"
dd if=$KDUMP_DUMPDEV of=$coredir/vmcore count=$dumpsize
invalidate_dumpdev
}
case "$1" in
start)
if is_crash_kernel; then
if [ -z "$KDUMP_DUMPDEV" ] ; then
if [ -n "$KDUMP_TRANSFER" ]; then
$KDUMP_TRANSFER
else
save_core
fi
fi
if test "$KDUMP_IMMEDIATE_REBOOT" = "yes"; then
/sbin/reboot
# sleep to avoid the conflict with script "single"
sleep 600
fi
else
if have_valid_dump_in_dumpdev ; then
copy_dump_from_dumpdev
fi
load_kdump
fi
;;
stop)
if [ ! -f /proc/vmcore ]; then
if [ "$RUNLEVEL" != "" ]; then
echo -n "Not unloading kdump during runlevel changes"
rc_status -s
else
echo -n "Unloading kdump"
$KEXEC -p -u
rc_status -v
fi
fi
;;
status)
echo "not implemented"
;;
restart|reload)
$0 stop
$0 start
;;
condrestart)
;;
*)
echo $"Usage: $0 {start|stop|status|restart|reload}"
exit 1
esac
exit $?