#!/bin/sh -e

# This script is run in a dump capture kernel to save a kernel dump to an
# SD card or HDD.
#
# (C) Copyright 2015 Axis Communications AB, LUND, SWEDEN
# This file is released under the GPL v2.

PATH=/sbin:/bin:/usr/sbin:/usr/bin
DEVPATH=
SD_MOUNT=/tmp/sdcard
max_num_dumps=5
conffile=/etc/sysconfig/crashdump.conf
MODE=${1:-}

greet() {
	echo "$0: $*"
}

whine() {
	greet "$*" >&2
}

restart() {
	[ $# -gt 0 ] && [ "$1" ] && { case $1 in (''|[!01]) ! :; esac; } ||
		panic "restart: Invalid argument"

	greet "Rebooting"
	reboot -ff || whine "Failed to reboot"

	sleep 5
	exit $1
}

unmount_sd() {
	! grep -q $SD_MOUNT /proc/mounts || {
		umount $SD_MOUNT || {
			whine "Unable to unmount storage;" 			      "attempt remount, read-only"
			mount -o remount,ro $SD_MOUNT ||
				whine "Unable to remount storage read-only"
		}
	}
}

checkmount() {
	[ $# -eq 2 ] && [ "$1" ] && [ "$2" ] ||
		panic "checkmount: invalid arguments"

	case "$MOUNTS" in *" $1 $2 "*) :; *) ! :; esac ||
		mount -t $2 nodev $1 || panic "Unable to mount $1"
}

panic() {
	whine "$*!"
	unmount_sd
	restart 1
}

greet "Kernel crash dump"

pm=/proc/mounts
[ -r $pm ] || mount -t proc proc /proc || panic "Failed to mount /proc"
MOUNTS=$(cat $pm) && [ "$MOUNTS" ] || panic "Unable to get mounts"

checkmount /sys sysfs
checkmount /dev devtmpfs
checkmount /run tmpfs
checkmount /var/volatile tmpfs
mkdir -p /var/volatile/tmp

echo 600 > /sys/module/self_destruct/parameters/timeout ||
	panic "Unable to set self-destruct"

/sbin/fwmgr init || panic "Failed to setup firmware volumes"

[ ! -r $conffile ] || exec /lib/systemd/systemd --unit=crashdump.service

watchdog -T 600 -t 1200 /dev/watchdog || panic "Unable to start watchdog"

[ -d $SD_MOUNT ] || mkdir -p $SD_MOUNT || panic "Failed to create $SD_MOUNT"

sd_activate=/usr/share/temperature_ctrld/sd_activate.sh
[ -f $sd_activate ] && {
	$sd_activate force || panic "Unable to activate SD card"
}

modprobe ahci_platform 2>/dev/null || :

[ $(ls /sys/class/mmc_host/ 2>/dev/null | wc -l) -lt 2 ] || {
	greet "Waiting for all MMC controllers to stabilize"
	sleep 5
}

timeout=10
count=0
while :; do
	for dev in mmcblk0p1 mmcblk1p1 mmcblk2p1 sda1; do
		[ -b /dev/$dev ] || continue

		case $dev in
			mmc*)
				# Check that the MMC device is an SD card
				partsysfs=$(find /sys/devices/ -name $dev | head -1) || continue
				read type <$partsysfs/../device/type || continue
				[ "$type" = SD ] || continue

				trigger=${partsysfs##*mmc_host/}
				trigger=${trigger%%/*}

				echo $trigger >/sys/class/leds/status:red/trigger 2>/dev/null ||
					whine "Unable to write LED trigger $trigger"
				;
		esac

		DEVPATH=/dev/$dev
		break
	done

	[ -z "$DEVPATH" ] || break

	[ $count -lt $timeout ] ||
		panic "Unable to find storage"

	greet "Will wait $(($timeout - $count)) more second(s) for storage"
	count=$(($count + 1))
	sleep 1
done

greet "Mounting $DEVPATH at $SD_MOUNT"

cryptomount() {
	local kw=passphrase=
	grep -q $kw /etc/storage/storages.conf || return
	greet "Mounting encrypted SD-card"
	printf "$(sed -ne "/^$kw/{s///;s/%/%%/g;p;q}" /etc/storage/storages.conf)" >/tmp/key
	cryptsetup open $DEVPATH foo --key-file /tmp/key || return
	mount /dev/mapper/foo $SD_MOUNT
}

fmtmount() {
	case $MODE in storage+format) :; *) ! :; esac || return
	greet "Formatting $DEVPATH"
	mkfs.ext4 -U 281e9e0e-129d-4cb4-868d-843e7b9d39bd -E hash_seed=9c1b5092-9e76-4791-96e1-c1c4a2ee3447 -F -L AxisDump $DEVPATH || return
	mount $DEVPATH $SD_MOUNT
}

mount $DEVPATH $SD_MOUNT || cryptomount || fmtmount ||
	panic "Failed to mount $DEVPATH"

dumpsd=$SD_MOUNT/dumps
[ -d $dumpsd ] || mkdir -p $dumpsd || panic "Unable to make dumps directory"

[ $(echo $dumpsd/dump-* | wc -w) -lt $max_num_dumps ] || {
	olddumps=$(find $dumpsd -type f -mtime +0) ||
		whine "Unable to find old dumps"
	[ -z "$olddumps" ] || {
		greet "Trying to delete dumps older than 1 day: $olddumps"
		rm -f $olddumps || whine "Unable to delete dumps"
	}
}

[ $(echo $dumpsd/dump-* | wc -w) -lt $max_num_dumps ] ||
	panic "Too many existing dumps"

vmcore=/proc/vmcore
now=$(date +%Y%m%d-%H%M%S)
dumpfile=$dumpsd/dump-$now.elf

if command -v lzop >/dev/null 2>&; then
	cmd="cat $vmcore | lzop -1 >$dumpfile.lzo"
else
	cmd="cp $vmcore $dumpfile"
fi

greet "Running $cmd"
eval $cmd || panic "Unable to save dump"

etb=/dev/*.etb
[ ! -r $etb ] || {
	greet "Saving ETB"
	cp $etb $dumpsd/etb-$now || whine "Unable to save ETB"
}

greet "Syncing $DEVPATH"
sync || whine "Unable to sync"

greet "Unmounting $DEVPATH"
unmount_sd

greet "Done"
restart 0