#!/bin/bash

SERVER=coroner.search.yandex.net
PORT=6666

CFG="/etc/netconsole.cfg"
NETCONSOLE=""
VERBOSITY="quiet"
VRF=""
DEV_FORCE=""
USE_V4=""

if [ -n "$CFG" -a -f $CFG ] ; then
	. $CFG
fi

# keep last route and verbosity
CON="$NETCONSOLE"
OPT="$VERBOSITY"

get_device_driver()
{
	DEV=$1
	DEVPATH=/sys/class/net/${DEV}/device/driver/module
	if [ -d "${DEVPATH}" ]; then
		MOD=$(basename "$(readlink "${DEVPATH}")")
	else
		MOD=$(ethtool -i "$DEV" | grep driver | cut -d " " -f 2)
	fi
	echo "$MOD"
}

case "$1" in
	enable|on)
		if [ "$OPT" = "disabled" ] ; then
			CON=""
			OPT="quiet"
		fi
		;;
	disable|off)
		OPT="disabled"
		;;
	start)
		;;
	stop)
		OPT="stopped"
		;;
	status)
		if [ -e /sys/module/netconsole ] ; then
			echo "netconsole: $VERBOSITY $SERVER:$PORT $NETCONSOLE"
		elif [ "$VERBOSITY" = "disabled" ] ; then
			echo "netconsole: disabled"
		else
			echo "netconsole: stopped"
		fi
		exit 0
		;;
	update)
		if [ ! -e /sys/module/netconsole -a "$OPT" != "disabled" ] ; then
			OPT="stopped"
		fi
		CON=""
		;;
	quiet)
		OPT="quiet"
		;;
	verbose)
		OPT="verbose"
		;;
	debug)
		OPT="debug"
		;;
	initrd)
		;;
	*)
		echo "usage: $0 < enable | disable | start | stop | status | update | quiet | verbose | debug | initrd >"
		exit 2
		;;
esac

if [ "$OPT" != "stopped" -a "$OPT" != "disabled" ] ; then
	word() {
		sed -ne 's#.*'$1'\s\(\S\+\).*#\1#p' | head -1
	}
	qtype="AAAA"
	if [ -n "$USE_V4" ]; then
	    qtype="A"
	fi

	if [ -n "$VRF" ]; then
		DST=$(ip vrf exec "$VRF" host -t "$qtype" "$SERVER" | word 'address')
		RT=$(ip route get "$DST" vrf "$VRF")
	else
		DST=$(host -t "$qtype" "$SERVER" | word 'address')
		RT=$(ip route get "$DST")
	fi
	DEV=$(echo "$RT" | word 'dev')
	SRC=$(echo "$RT" | word 'src')
	GW=$(echo "$RT" | word 'via')
	[ -z "$GW" ] && GW="$DST"
	ping6 -c 1 "$DST" >/dev/null 2>&1
	if [ -n "$VRF" ]; then
		MAC=$(ip neigh show to "$GW" dev "$DEV" vrf "$VRF" | word 'lladdr')
	else
		MAC=$(ip neigh show to "$GW" dev "$DEV" | word 'lladdr')
	fi

	if [ -n "$SRC" -a -n "$DEV" -a -n "$DST" -a -n "$MAC" ] ; then
		CON="${PORT}@${SRC}/${DEV_FORCE:-$DEV},${PORT}@${DST}/${MAC}"
	else
		DEV=${CON%%,*}
		DEV=${DEV##*/}
	fi

	if [ -z "$CON" ] ; then
		echo "netconsole: cannot find route to ${SERVER}:${PORT}"
		exit 2
	fi
	MOD=$(get_device_driver "$DEV")
	if [ "$MOD" = 'mlx5_core' -a $(uname -r) = '4.4.171-70.1' ] ; then
		echo "netconsole: broken netpoll in $MOD #KERNEL-270"
		OPT="stopped"
	fi
fi

if [ "$1" = "initrd" ] ; then
	echo Adding netconsole into /etc/initramfs-tools/modules
	grep -lq "$MOD" /etc/initramfs-tools/modules || ( printf "\n# Netconsole:\n$MOD\n" >> /etc/initramfs-tools/modules )
	sed -e '/^netconsole/d' -i /etc/initramfs-tools/modules
	printf "netconsole netconsole=$CON\n" >> /etc/initramfs-tools/modules
	update-initramfs -u -k all
	exit 0
fi

# submit pstore on start or update unless disabled
if [ "$1" = "start" -o "$1" = "update" ] && [ "$OPT" != "disabled" ] &&
   grep -q /sys/fs/pstore /proc/self/mountinfo ; then
	for REPORT in $(find /sys/fs/pstore -type f -name 'dmesg-*' | sort) ; do
		ping6 -c 3 "$SERVER" >/dev/null 2>&1 || break

		echo "netconsole: uploading pstore report $REPORT"

		(	echo "--- BEGIN OF PSTORE $REPORT ---"
			ls -l "$REPORT"
			cat "$REPORT"
			echo
			echo "--- END OF PSTORE $REPORT ---"
		) > /dev/udp/$SERVER/$PORT

		(	ls -l "$REPORT"
			cat "$REPORT"
		) | logger -p local0.emerg -t pstore

		mkdir -p /var/log/pstore
		cp --preserve=all "$REPORT" /var/log/pstore

		rm -f "$REPORT"
	done
fi

/sbin/modinfo netconsole >/dev/null 2>&1 || exit 0

if [ -e /sys/module/netconsole ] ; then
	[ "$CON" = "$NETCONSOLE" -a "$OPT" = "$VERBOSITY" -a "$OPT" != "disabled" ] && exit 0
	/sbin/rmmod netconsole
fi

if [ "$VERBOSITY" = "debug" ] ; then
	echo N > /sys/module/printk/parameters/ignore_loglevel
fi

case "$OPT" in
	disabled)
		;;
	stopped)
		exit 0
		;;
	quiet)
		PARAMETERS="oops_only=1"
		if /sbin/modinfo -p netconsole | grep -q '^dump_only' ; then
			PARAMETERS="dump_only=1"
		fi
		;;
	verbose)
		PARAMETERS=""
		;;
	debug)
		PARAMETERS=""
		echo Y > /sys/module/printk/parameters/ignore_loglevel
		;;
esac

if [ "$OPT" != "disabled" ] ; then
	echo "netconsole: $OPT $SERVER:$PORT $CON $PARAMETERS"
	/sbin/modprobe netconsole "netconsole=$CON" $PARAMETERS
fi

# Update config if auto-update config isn't disabled
if [ -n "$CFG" -a ${AUTOUPDATE:-1} = 1 ] ; then
cat >"${CFG}.tmp" <<EOF
NETCONSOLE="$CON"
VERBOSITY="$OPT"
VRF="$VRF"
DEV_FORCE="$DEV_FORCE"
USE_V4="$USE_V4"
EOF
mv "${CFG}.tmp" "${CFG}"
fi
