#! /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:
# Short-Description: kdump core saving and boot configuration
# 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
KDUMP_CLEANUP_DUMPS=/usr/sbin/kdump-cleanup_dumps
KDUMP_SAVE_DUMP=/usr/sbin/kdump-save_dump
KDUMP_IDENTIFY_KERNEL=/usr/sbin/kdump-identify_kernel

BOOTDIR="/boot"

# The default dumper
#
# Clean up old stuff if necessary, check the free size
# and save the vmcore
save_core()
{
    $KDUMP_CLEANUP_DUMPS
    if [ "$?" -ne 0 ] ; then
	rc_status -v
	return 1
    fi

    $KDUMP_SAVE_DUMP /proc/vmcore
    rc_status -v

    return 0
}

# print the available kdump kernel path
# empty if no matching file is found
check_boot_kernel ()
{
    local kstr

    # if the kernel is relocatable, try vmlinuz first because
    # currently there are bugs with relocatable ELF-Images
    # (ET_DYN => GDB doesn't work / ET_EXEC => kexec-tools doesn't
    # detect)
    case `uname -i` in
    x86_64|i386)
	kstr="${BOOTDIR}/vmlinuz-$1$2"
	if [ -f $kstr ] ; then 
	    if $KDUMP_IDENTIFY_KERNEL -r $kstr &>/dev/null ; then
		echo $kstr
		return
	    fi
	fi
    esac

    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 reset_devices"
	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

    # remove `--args-linux' for x86 type kernel files here
    if [ "$($KDUMP_IDENTIFY_KERNEL -t $kdump_kernel)" = "x86" ] ; then
	KEXEC_OPTIONS=$(echo $KEXEC_OPTIONS | sed -e 's/--args-linux//g')
    fi

    KEXEC_CALL="$KEXEC -p $kdump_kernel --append=\"$KDUMP_COMMANDLINE\""
    KEXEC_CALL="$KEXEC_CALL --initrd=$kdump_initrd $KEXEC_OPTIONS"

    if [ $(($KDUMP_VERBOSE & 1)) -gt 0 ] ; then
	logger -i -t kdump "Loading kdump kernel: $KEXEC_CALL"
    fi
    if [ $(($KDUMP_VERBOSE & 4)) -gt 0 ] ; then
	echo "Loading kdump kernel: $KEXEC_CALL"
    fi
		
    eval "$KEXEC_CALL"

    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
    grep -q elfcorehdr= /proc/cmdline && return 0
    return 1
}

# 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 &>/dev/null
}

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
		$KDUMP_SAVE_DUMP $KDUMP_DUMPDEV
		rc_status -v
		if [ "$?" -eq 0 ] ; then
		    invalidate_dumpdev
		    rc_status
		fi
            fi
	    load_kdump
	fi
	;;
  stop)
	if ! is_crash_kernel ; 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)
	if [ -r /sys/kernel/kexec_crash_loaded ]; then
	    if [ "$(cat /sys/kernel/kexec_crash_loaded)" = "1" ]; then
		echo "kdump kernel loaded"
	    else
		echo "kdump kernel not loaded"
	    fi
	else
	    echo "not implemented"
	fi
	;;
  restart|reload)
	$0 stop
	$0 start
	;;
  condrestart)
	;;
  *)
	echo $"Usage: $0 {start|stop|status|restart|reload}"
	exit 1
esac

exit $?

# vim: set ts=8 sw=4 sts=4 noet:
