#!/bin/bash
set -e -x -u

main () {
	parse_args "$@"
	if [ "$CRASHHOSTNAME" = "$(hostname -f)" ]
	then
		setup_collector
	else
		setup_crash_kernel
	fi
}

parse_args () {
	CRASHHOSTNAME=${1:?FQDN of crash capture host}
	craship=$(dig +short +search "$CRASHHOSTNAME")
	dev=$(ip route get "$craship" | sed -rne 's,.* dev (\w+) +src .*,\1,p')
	driver=$(basename "$(readlink /sys/class/net/eth0/device/driver)")
}

setup_collector () {
	# Create user and service for collecting data from the network:
	adduser --system crash --home /var/lib/crash

	create_file /var/lib/crash/collector.sh <<'__SH__'
#!/bin/sh
umask 0177
exec cat >/var/lib/crash/core-$(date +%s).gz
__SH__
	update-inetd --add '666\tstream\ttcp\tnowait\tcrash\t/var/lib/crash/collector.sh'

	# Make sure the port is allowed by the firewall:
	ucr set security/packetfilter/tcp/666/all=ACCEPT
	service univention-firewall restart
}

setup_crash_kernel () {
	install_kexec
	create_initramfs_hook
	create_initramfs_modules
	create_initramfs_script
	create_initramfs
	setup_grub
	setup_kernel
	setup_boot
	finish
}
install_kexec () {
	which kexec >/dev/null 2>/dev/null && return
	ucr set repository/online/unmaintained=yes
	apt-get -qq update
	apt-get -qq install kexec-tools
	sed -i -e '/^LOAD_KEXEC=/s/true/false/' /etc/default/kexec
	kexec -u || :
}
create_initramfs_hook () {
	create_file /etc/initramfs-tools/hooks/crash <<'__SH__'
#!/bin/sh
PREREQ=
prereqs () {
echo "$PREREQ"
}
case "$1" in
prereqs)
 prereqs
 exit 0
 ;;
esac
. /usr/share/initramfs-tools/hook-functions
copy_exec /bin/ip
copy_exec /bin/nc
__SH__
}
create_initramfs_modules () {
	grep -qFe "$driver" /etc/initramfs-tools/modules && return
	echo "$driver" >>/etc/initramfs-tools/modules
	update_initramfs=true
}
create_initramfs_script () {
	create_file /etc/initramfs-tools/scripts/crash <<__SH__
#!/bin/sh
trap 'sh </dev/console >/dev/console 2>/dev/console' EXIT
echo "Loading network driver '$driver'..."
modprobe "$driver"
echo "Configuring network interface '$dev'..."
ip addr add "$(ip addr show dev "$dev" | sed -rne 's,^.*inet ([0-9./]+) brd.*,\1,p')" dev "$dev"
ip link set "$dev" up
ip route add default via "$(ip route list | sed -rne 's,^default via ([0-9./]+) dev .*,\1,p')"
echo "Dumping crash to '$craship'..."
gzip -c </proc/vmcore | nc "$craship" 666
echo "Done. Rebooting..."
sleep 10
echo b >/proc/sysrq-trigger
__SH__
}
create_initramfs () {
	"$update_initramfs" || return
	# kernel + initramfs + uncompressed <= 128 MB !
	ucr set initramfs/modules=dep
	update-initramfs -u
}
setup_grub () {
	grub=$(ucr get grub/append)
	case "$grub" in
	*crashkernel=*) ;;
	*) ucr set grub/append="$grub crashkernel=128M" ;; # 384M-2G:64M,2G-:128M
	esac
	ucr set grub/gfxmode= grub/gfxpayload= grub/bootsplash=nosplash grub/loglevel=7 grub/quiet=no
}
setup_kernel () {
	create_file /etc/sysctl.d/crash.conf 0644 <<__CONF__
kernel.hung_task_panic=0          # panic when hung task is detected
kernel.panic_on_io_nmi=0          # panic on NMIs from I/O
kernel.panic_on_oops=1            # panic on oops or kernel bug detection
kernel.panic_on_unrecovered_nmi=1 # panic on NMIs from memory or unknown
kernel.softlockup_panic=0         # panic when soft lockups are detected
vm.panic_on_oom=0                 # panic when out-of-memory happens
__CONF__
}
setup_boot () {
	create_file /etc/rc.local '' FORCE <<'__SH__'
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

load_crash () {
	[ "$(cat /sys/kernel/kexec_crash_loaded)" = 0 ] || return
	console=--reset-vga
	args='1 irqpoll maxcpus=1 reset_devices boot=crash debug break=init nosplash'
	set -- $(cat /proc/cmdline)
	while [ $# -ge 1 ]
	do
		case "$1" in
		blacklist=*) args="${args} $1" ;;
		boot=*) ;;
		BOOTIF=*) args="${args} $1" ;;
		BOOT_IMAGE=*) ;;
		break|break=*) ;;
		console=ttyS*) args="${args} $1" console=--console-serial ;;
		console=*) args="${args} $1" ;;
		crashkernel=*) ;;
		drop_capabilities=*) ;;
		fastboot|forcefsck|fsckfix) ;;
		loglevel=*) args="${args} $1" ;;
		netconsole=*) args="${args} $1" ;;
		nfsroot=*|ip=*) ;;
		panic=*) ;;
		quiet|verbose|debug|debug=*) ;;
		resume=*|resume_offset=*|noresume) ;;
		root=*|rootflags=*|rootfstype=*|rootdelay=*) ;;
		ro|rw) ;;
		splash|nosplash) ;;
		*) echo "Unknown arg '$1'" ;;
		esac
		shift
	done

	kexec -p /boot/vmlinuz-`uname -r` --initrd /boot/initrd.img-`uname -r` --append="$args" $console
	cat /sys/kernel/kexec_crash_loaded
}
load_crash
:
__SH__
}
finish () {
	if [ "$(cat /sys/kernel/kexec_crash_size)" = 0 ]
	then
		echo "Now 'reboot'; after that test with 'echo c >/proc/sysrq-trigger'"
	else
		if [ "$(cat /sys/kernel/kexec_crash_loaded)" != 0 ]
		then
			kexec -p -u
		fi
		/etc/rc.local
		echo "Test with 'echo c >/proc/sysrq-trigger'"
	fi
}

update_initramfs=false
create_file () {
	[ -z "${3:-}" ] && [ -f "$1" ] && return 0
	cat >"$1"
	chmod "${2:-0755}" "$1"
	case "$1" in
	*/initramfs-tools/*) update_initramfs=true ;;
	esac
}

main "$@"
