#!/bin/sh
#
# Crash dump initialization script.
#
# Copyright 1999 Silicon Graphics, Inc. All rights reserved.
#

###########################################################################
# Configuration Variables
###########################################################################
CONFIG_FILE=/etc/sysconfig/dump
LCRASH=/sbin/lcrash
MAP=/boot/System.map
KERNTYPES=/boot/Kerntypes
LKCD_KSYMS=/sbin/lkcd_ksyms
DUMPCONFIG=/sbin/lkcd_config
DUMPSYSDEVICE=/dev/dump

###########################################################################
# Functions
###########################################################################
#
# Name: config_lkcd()
# Func: Configure the kernel crash dump mechanism.
#
config_lkcd()
{
	if [ ! -e $DUMPCONFIG ] ; then
		echo "Error: $DUMPCONFIG does not exist!" >&2
		exit 1
	fi

	# make sure dump variables exist
	if [ ! -e $DUMPSYSDEVICE ] ; then
		echo "DUMPSYSDEVICE does not exist!" >&2
		exit 1
	fi

	if [ ! "$DUMP_LEVEL" ] ; then
		echo "DUMP_LEVEL not defined in $CONFIG_FILE!" >&2
		exit 1
	fi

	if [ ! "$DUMP_COMPRESS" ] ; then
		echo "DUMP_COMPRESS not defined in $CONFIG_FILE!" >&2
		exit 1
	fi

	if [ ! "$DUMP_FLAGS" ] ; then
		echo "DUMP_FLAGS not defined in $CONFIG_FILE!" >&2
		exit 1
	fi

	if [ ! "$PANIC_TIMEOUT" ] ; then
		echo "PANIC_TIMEOUT not defined in $CONFIG_FILE!" >&2
		exit 1
	fi

	# set up the configurables in /proc.
	echo "$PANIC_TIMEOUT" > /proc/sys/kernel/panic

	# now run dumpconfig
	$DUMPCONFIG -d $DUMPDEV -l $DUMP_LEVEL -f $DUMP_FLAGS -c $DUMP_COMPRESS > /dev/null 2>&1

	if [ $? -ne 0 ] ; then
		echo "$DUMPCONFIG failed!" >&2
	fi

	return
}

#
# Name: save_lkcd()
# Func: Save the crash dump to the appropriate location.
#
save_lkcd()
{
	if [ ! "$DUMP_SAVE" ] ; then
		echo "DUMP_SAVE not defined in $CONFIG_FILE!" >&2
		exit 1
	fi

	# look for lcrash
	if [ ! -e $LCRASH ] ; then
		echo "Error: $LCRASH does not exist!" >&2
		exit 1
	fi

	# get the bounds value
	if [ ! -e "$DUMPDIR/bounds" ] ; then
		BOUNDS=0
	else
		BOUNDS=`cat $DUMPDIR/bounds` 2>&1
		if [ ! "$BOUNDS" ] ; then
			BOUNDS=0
		fi
	fi

	# save the crash dump
	if [ $DUMP_SAVE -eq 1 ] ; then
		$LCRASH -P -s $DUMPDEV $DUMPDIR 2>&1
		if [ $? -ne 0 ] ; then
			echo "Error: could not save crash dump." >&2
			exit 1
		elif [ ! -e "$DUMPDIR/dump.$BOUNDS" ] ; then
			return
		fi
	fi

	if [ ! -e $MAP ] ; then
		echo "Error: map file $MAP does not exist." >&2
		return
	elif [ ! -e $KERNTYPES ] ; then
		echo "Error: kerntypes file $KERNTYPES does not exist." >&2
		return
	else 
		mkdir $DUMPDIR/$BOUNDS
		if [ $? -ne 0 ] ; then
			echo "Error: unable to create directory $DUMPDIR/$BOUNDS." >&2
			return
		fi

		cd $DUMPDIR/$BOUNDS
		if [ -f $DUMPDIR/dump.$BOUNDS ] ; then
			/bin/mv $DUMPDIR/dump.$BOUNDS $DUMPDIR/$BOUNDS/
		fi
		/bin/cp -f $MAP $DUMPDIR/$BOUNDS/map.$BOUNDS
		/bin/cp -f $LCRASH $DUMPDIR/$BOUNDS/lcrash.$BOUNDS
		/bin/cp -f $KERNTYPES $DUMPDIR/$BOUNDS/kerntypes.$BOUNDS

		local RET=0
		echo -n "Generating crash report - this may take a few minutes"
		if [ ! -e /usr/bin/ksymoops ] ; then
			echo "Warning: ksymoops not found in /usr/bin." >&1
			RET=1
		else
			$LKCD_KSYMS map.$BOUNDS dump.$BOUNDS kerntypes.$BOUNDS \
				> temp_ksyms 2>/dev/null
			if [ $? -ne 0 ] ; then
				RET=1
			else
				echo | /usr/bin/ksymoops -s temp_map -k temp_ksyms -L \
					-m $MAP -o /lib/modules/`uname -r`/ 1>/dev/null
				awk '{print $1,$2,$4$3}' temp_map > map.$BOUNDS
				/bin/rm -f temp_ksyms
        		        /bin/rm -f temp_map
			fi
		fi

		$LCRASH -r map.$BOUNDS dump.$BOUNDS kerntypes.$BOUNDS \
			> analysis.$BOUNDS 2>&1
		if [ $? -ne 0 ] ; then
			echo "Error: could not create crash report." >&2
			exit 1
		fi
		if [ $RET -eq 1 ] ; then
			echo "Cannot include module symbols in map.$BOUNDS." >&2
			exit 1
		fi
	fi

	return
}

#
# Name: usage()
# Func: Print out usage information and exit (with an exit code of 1)
#
usage()
{
	echo "Usage: $0 <config|save>" >&2
	exit 1
}

#
# Name: check_root()
# Func: Verify that the caller of this script is root.
#
check_root()
{
	kill -s 0 1 2>&-
	if [ $? = 1 ]; then
		echo "Error: this script must be run by root." >&2
		return 0
	fi
	return 1
}

###########################################################################
# Main Script
###########################################################################

# make sure the user is root.
check_root
if [ $? = 0 ] ; then
	exit 1
fi

# make sure the $CONFIG_FILE exists.
if [ ! -e "$CONFIG_FILE" ] ; then
	echo "Error: $CONFIG_FILE missing or is not executable!" >&2
	exit 1
fi

# source the configuration file.
. $CONFIG_FILE

if [ ! "$DUMP_ACTIVE" ] ; then
	echo "DUMP_ACTIVE not defined in $CONFIG_FILE!" >&2
	exit 1
fi

# bail out if dumps are not active.
if [ $DUMP_ACTIVE -eq 0 ] ; then
	exit 0
fi

# make sure dump variables exist
if [ ! "$DUMPDEV" ] ; then
	echo "DUMPDEV not defined in $CONFIG_FILE!" >&2
	exit 1
fi

# make sure $DUMPDEV exists
if [ ! -e "$DUMPDEV" ] ; then
	# make sure /etc/fstab exists (unrealistic check)
	if [ ! -e /etc/fstab ] ; then
		echo "/etc/fstab does not exist!" >&2
		exit 1
	fi

	# get the primary SCSI swap partition out of /etc/fstab
	primary_dumpdev=`cat /etc/fstab | grep -v '^#' | \
		awk '{ if ($2 == "swap") { print $1 } }' | \
		head -1`

	if [ ! "$primary_dumpdev" ] ; then
		# if no swap partition exists, bail out
		echo "No swap partition exists for crash dumps!" >&2
		exit 0
	else
		# make the link from /dev/{whatever} to /dev/vmdump
		ln -s $primary_dumpdev $DUMPDEV
	fi
fi

# make sure we remove older block dump devices
if [ -b $DUMPSYSDEVICE ] ; then
	/bin/rm -f $DUMPSYSDEVICE
fi

# make sure system dump device exists -- otherwise, make it
if [ ! -e $DUMPSYSDEVICE ] ; then
	mknod $DUMPSYSDEVICE c 227 0
	chmod 644 $DUMPSYSDEVICE
fi

# make sure dump variables exist
if [ ! "$DUMPDIR" ] ; then
	echo "DUMPDIR not defined in $CONFIG_FILE!" >&2
	exit 1
fi

# make sure $DUMPDIR exists
if [ ! -d "$DUMPDIR" ] ; then
	mkdir -p $DUMPDIR
	if [ $? -ne 0 ] ; then
		echo "Error: unable to create directory $DUMPDIR!" >&2
	exit 1
	fi
fi

# parse each argument.
if [ "$1" = "config" ] ; then
	config_lkcd
elif [ "$1" = "save" ] ; then
	save_lkcd
else
	usage
fi

# leave (with success)
exit 0
