#!/bin/sh

# Copyright (c) 2013 Colin Percival
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$

# PROVIDE: panicmail
# REQUIRE: savecore mail

# Add the following lines to /etc/rc.conf to enable panicmail:
#
# panicmail_enable (bool):	Set to "NO" by default.
#				Set it to "YES" to enable panicmail.
#
# panicmail_autosubmit (bool):	Set to "NO" by default.
#				Set it to "YES" to automatically submit panic
#				emails instead of sending them to root for
#				review first.
#
# panicmail_sendto (str):	Set to
#				"FreeBSD Panic Reporting <cperciva+panicmail@freebsd.org>"
#				by default.
#				Change to your desired panic submission target.
#
# panicmail_key (str):		Set to "/usr/local/etc/cperciva-panicmail.pem"
#				by default.
#				Change to the encryption key for your panic
#				submission target.
: ${panicmail_enable:="NO"}
: ${panicmail_autosubmit:="NO"}
: ${panicmail_sendto:="FreeBSD Panic Reporting <cperciva+panicmail@freebsd.org>"}
: ${panicmail_key:="/usr/local/etc/cperciva-panicmail.pem"}

. /etc/rc.subr

name="panicmail"
rcvar="panicmail_enable"
start_cmd="panicmail_run"
stop_cmd=":"

# On HEAD, gdb will be installed from ports
PATH=${PATH}:/usr/local/bin

# Gather the data we want to include in a panic report.
panicmail_gather()
{
	local tmpfile
	tmpfile=`mktemp` || exit 1

	# We want the dump header.
	cat "${dumpdir}/info.$1" > "${dumpdir}/panicmail.$1"
	echo >> "${dumpdir}/panicmail.$1"

	# And we want a backtrace (we should be able to pipe the commands
	# directly into kgdb, but that doesn't work with our /bin/sh):
	echo "Backtrace:" >> "${dumpdir}/panicmail.$1"
	echo bt > "${tmpfile}"
	echo quit >> "${tmpfile}"
	kgdb -q "`sysctl -n kern.bootfile`" "${dumpdir}/vmcore.$1" \
	    < "${tmpfile}" >> "${dumpdir}/panicmail.$1" 2> /dev/null
	echo >> "${dumpdir}/panicmail.$1"

	# Remove the temporary file.
	rm "${tmpfile}"
}

# Encrypt the information in the panic report.
panicmail_encrypt()
{
	local tmpfile
	tmpfile=`mktemp` || exit 1

	# Encrypt using pkesh.
	/usr/local/bin/pkesh enc "$2" "${dumpdir}/panicmail.$1" "${tmpfile}" || exit 1

	# Add extra armour.
	echo "-----ENCRYPTED FREEBSD PANIC DATA STARTS HERE---------------------" > "${dumpdir}/panicmail.$1.enc"
	lam -s '|' "${tmpfile}" -s '|' >> "${dumpdir}/panicmail.$1.enc"
	echo "-----ENCRYPTED FREEBSD PANIC DATA ENDS HERE-----------------------" >> "${dumpdir}/panicmail.$1.enc"

	# Remove the temporary file.
	rm "${tmpfile}"
}

# Construct an email destined for root to review and forward.
panicmail_root()
{

	cat <<-EOF
		To: root
		From: ${panicmail_sendto}
		Subject: Kernel panic

		A kernel panic has occurred on this system.  You can assist in
		debugging this by allowing some information to be reported
		about this panic.

		The following information is contained in the encrypted panic
		report at the end of this email:

	EOF
	lam -s "> " "${dumpdir}/panicmail.$1"
	cat <<-EOF

		If you are happy to have this information submitted (i.e., it
		does not contain any information you want kept private), please
		submit the following ASCII armoured block to
		  ${panicmail_sendto};
		you should be able to do this by hitting "Reply" in your mail
		client and removing everything up to this point.

	EOF
	cat "${dumpdir}/panicmail.$1.enc"
}

# Construct an email headed directly to the panic submission target.
panicmail_auto()
{

	cat <<-EOF
		To: ${panicmail_sendto}
		From: root
		Subject: Kernel panic

	EOF
	cat "${dumpdir}/panicmail.$1.enc"
}

panicmail_run()
{
	local nr

	# Set umask; we may create files with sensitive data.
	umask 077

	# Quit if we have no dumps.
	if ! [ -f "${dumpdir}/bounds" ]; then
		return 0
	fi

	# If we have info.last, use that to figure out the last dump number.
	if [ -e "${dumpdir}/info.last" ]; then
		nr=`readlink "${dumpdir}/info.last"`
		nr=${nr##*.}
	else
		# Otherwise, get the number from bounds.
		nr=$((`cat "${dumpdir}/bounds"` - 1))
	fi

	# Make sure the dump actually exists.
	if ! [ -f "${dumpdir}/info.${nr}" ] ||
	    ! [ -f "${dumpdir}/vmcore.${nr}" ]; then
		return 0
	fi

	# Have we already sent an email about this one?  We compare times in
	# order to catch the case where dump numbers repeat.
	if [ "${dumpdir}/panicmail.${nr}" -nt "${dumpdir}/vmcore.${nr}" ]; then
		return 0
	fi

	# Gather information about this panic.
	panicmail_gather "${nr}"

	# Encrypt the panic information.
	panicmail_encrypt "${nr}" "${panicmail_key}"

	# Generate and send an email.
	if checkyesno panicmail_autosubmit; then
		panicmail_auto "${nr}" | sendmail -t
	else
		panicmail_root "${nr}" | sendmail -t
	fi
}

load_rc_config $name
run_rc_command "$1"
