#!/bin/sh
### BEGIN INIT INFO
# Provides:          xen
# Required-Start:    $syslog $remote_fs
# Required-Stop:     $syslog $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Xen daemons
# Description:       Xen daemons
### END INIT INFO

# Important notes to keep in mind when changing this script:
#
# * This init script is part of the xen-utils-common package.
# * It is *not* called from xen-utils-common maintscripts, but from the
#   xen-utils-V package maintscripts instead, and only if V is equal to the
#   currently running version of Xen.
# * The xen-utils-V package has a dependency on xen-utils-common >= V.
# * So, while there can be multiple xen-utils-V packages installed, there is
#   still only a single xen-utils-common package installed, which matches the
#   newest installed Xen version.
# * This means that this script has to be compatible with the other packages
#   for any older Xen version that was available in the current and previous
#   Debian release.
#
# Example scenarios in which this is needed:
#
# * After upgrading to a new Xen version, the new init script will already be
#   used for shutting down the system before reboot.
# * If the user encounters problems and reboots back into the old Xen version,
#   this init script will also be used.

. /lib/init/vars.sh
. /lib/lsb/init-functions

# Default variables
XENSTORED_DIR="/run/xenstored"

[ -r /etc/default/xen ] && . /etc/default/xen

PATH=/sbin:/bin:/usr/sbin:/usr/bin
DESC="Xen daemons"

case "$(cat /sys/hypervisor/type 2>/dev/null)" in
xen)		;;
*)	exit 0	;; # not running under Xen
esac

VERSION=$(/usr/lib/xen-common/bin/xen-version)
ROOT=$(/usr/lib/xen-common/bin/xen-dir)
if [ $? -ne 0 ]; then
	log_warning_msg "No compatible Xen utils for Xen $VERSION"
	exit 1
fi

XENCONSOLED="$ROOT"/bin/xenconsoled
XENCONSOLED_PIDFILE="/run/xenconsoled.pid"
CXENSTORED="$ROOT"/bin/xenstored
OXENSTORED="$ROOT"/bin/oxenstored
XENSTORED_PIDFILE="/run/xenstore.pid"
QEMU=/usr/libexec/xen-qemu-system-i386
QEMU_PIDFILE="/run/qemu-dom0.pid"
QEMU_ARGS="-xen-domid 0 -xen-attach -name dom0 -nographic -M xenpv -daemonize -monitor none -serial none -parallel none"

modules_setup()
{
	modprobe xenfs 2>/dev/null
	modprobe xen-evtchn 2>/dev/null
	modprobe xen-gntdev 2>/dev/null
	modprobe xen-acpi-processor 2>/dev/null
}

xenfs_setup()
{
	[ -e "/proc/xen/capabilities" ] && return 0
	log_progress_msg "xenfs"
	[ -d "/proc/xen" ] || return 1
	mount -t xenfs xenfs /proc/xen || return 1
	return 0
}

capability_check()
{
	[ -e "/proc/xen/capabilities" ] || return 1
	grep -q "control_d" /proc/xen/capabilities || return 1
	return 0
}

env_setup()
{
	[ -d /run/xen ] && return 0

	mkdir -m 700 /run/xen
	[ -x /sbin/restorecon ] && /sbin/restorecon /run/xen
}

xenconsoled_start()
{
	log_progress_msg "xenconsoled"
	xenconsoled_start_real
	return $?
}

xenconsoled_stop()
{
	log_progress_msg "xenconsoled"
	xenconsoled_stop_real
	return $?
}

xenconsoled_restart()
{
	log_progress_msg "xenconsoled"
	xenconsoled_stop_real
	case "$?" in
		0|1)
		xenconsoled_start_real
		case "$?" in
			0) ;;
			*) return 2 ;;
		esac
		;;
		*) return 2 ;;
	esac
	return 0
}

xenconsoled_start_real()
{
	start-stop-daemon --start --quiet --pidfile "$XENCONSOLED_PIDFILE" --exec "$XENCONSOLED" --test > /dev/null \
		|| return 1
	start-stop-daemon --start --quiet --pidfile "$XENCONSOLED_PIDFILE" --exec "$XENCONSOLED" -- \
		$XENCONSOLED_ARGS --pid-file "$XENCONSOLED_PIDFILE" \
		|| return 2
}

xenconsoled_stop_real()
{
	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile "$XENCONSOLED_PIDFILE" --name xenconsoled
	RETVAL="$?"
	[ "$RETVAL" = 2 ] && return 2
	start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec "$XENCONSOLED"
	[ "$?" = 2 ] && return 2
	rm -f $XENCONSOLED_PIDFILE
	return "$RETVAL"
}

qemu_start()
{
	[ -x $QEMU ] || return 0
	log_progress_msg "qemu"
	qemu_start_real
	return $?
}

qemu_stop()
{
	[ -x $QEMU ] || return 0
	log_progress_msg "qemu"
	qemu_stop_real
	return $?
}

qemu_restart()
{
	[ -x $QEMU ] || return 0
	log_progress_msg "qemu"
	qemu_stop_real
	case "$?" in
		0|1)
		qemu_start_real
		case "$?" in
			0) ;;
			*) return 2 ;;
		esac
		;;
		*) return 2 ;;
	esac
	return 0
}

qemu_start_real()
{
	start-stop-daemon --start --quiet --pidfile "$QEMU_PIDFILE" --exec "$QEMU" --test > /dev/null \
		|| return 1
	start-stop-daemon --start --quiet --pidfile "$QEMU_PIDFILE" --exec "$QEMU" -- \
		$QEMU_ARGS -pidfile "$QEMU_PIDFILE" \
		|| return 2
}

qemu_stop_real()
{
	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile "$QEMU_PIDFILE" --exec "$QEMU"
	RETVAL="$?"
	[ "$RETVAL" = 2 ] && return 2
	rm -f $QEMU_PIDFILE
	return "$RETVAL"
}


xenstored_start()
{
	# First, we check if any of xenstored or oxenstored is already running. If
	# so, we abort and leave it alone.
	for try_xenstored in "$OXENSTORED" "$CXENSTORED"; do
		if [ -x $try_xenstored ]; then
			start-stop-daemon --start --quiet --pidfile "$XENSTORED_PIDFILE" \
				--exec "$try_xenstored" --test > /dev/null
			# s-s-d code 1 here means: "We found a matching live process!"
			if [ $? -eq 1 ]; then
				return 0
			fi
		fi
	done
	log_progress_msg "xenstored"
	[ -d "$XENSTORED_DIR" ] || mkdir -p "$XENSTORED_DIR"
	[ -x /sbin/restorecon ] && /sbin/restorecon "$XENSTORED_DIR"
	export XENSTORED_ROOTDIR="$XENSTORED_DIR"
	# If none of them are running, then try starting one. If the user made an
	# explicit choice, then run that. Else try the different xenstored
	# implementations we know about in order of preference.
	case "$XENSTORED" in
	?*) try_xenstoreds='XENSTORED' ;;
	'') try_xenstoreds='OXENSTORED CXENSTORED' ;;
	esac
	for try_xenstored_var in $try_xenstoreds; do
		eval "try_xenstored=\$$try_xenstored_var"
		if [ -x $try_xenstored ]; then
			if start-stop-daemon --start --quiet \
				--pidfile "$XENSTORED_PIDFILE" \
				--exec /usr/bin/choom -- -n -1000 "$try_xenstored" -- \
				$XENSTORED_ARGS --pid-file "$XENSTORED_PIDFILE"; then
					started_xenstored=$try_xenstored
					break
			fi
		fi
	done
	if [ -z "$started_xenstored" ]; then
		log_failure_msg "No suitable xenstored binary found to start."
		return 1
	fi

	# Wait for xenstored to actually come up, timing out after 30 seconds
	local time=0
	local timeout=30
	while [ $time -lt $timeout ] && ! `xenstore-read -s / >/dev/null 2>&1` ; do
	    time=$(( $time+1 ))
	    sleep 1
	done

	# Exit if we timed out
	if ! [ $time -lt $timeout ] ; then
	    return 2
	fi

	# Add some extra entries about dom0 into xenstore
	if [ -e $ROOT/bin/xen-init-dom0 ] ; then
	    $ROOT/bin/xen-init-dom0 > /dev/null
	else
	    # xen-init-dom0 has been present since Stretch.
	    # Remove this compatibility section in Buster+1
	    xenstore-write "/local/domain/0/name" "Domain-0"
	    xenstore-write "/local/domain/0/domid" "0"
	fi
}

case "$1" in
  start)
	log_daemon_msg "Starting $DESC"
	modules_setup
	xenfs_setup
	case "$?" in
		0) ;;
		*) log_end_msg 1; exit ;;
	esac
	capability_check
	case "$?" in
		0) ;;
		*) log_end_msg 0; exit ;; # not a dom0, skip the rest
	esac
	env_setup
	xenstored_start
	case "$?" in
		0) ;;
		*) log_end_msg 1; exit ;;
	esac
	xenconsoled_start
	case "$?" in
		0|1) ;;
		*) log_end_msg 1; exit ;;
	esac
	qemu_start
	case "$?" in
		0|1) ;;
		*) log_end_msg 1; exit ;;
	esac
	log_end_msg 0
	;;
  stop)
	capability_check
	case "$?" in
		0) ;;
		*) exit ;;
	esac
	log_daemon_msg "Stopping $DESC"
	ret=0
	qemu_stop
	case "$?" in
		0|1) ;;
		*) ret=1 ;;
	esac
	xenconsoled_stop
	case "$?" in
		0|1) ;;
		*) ret=1 ;;
	esac
	log_end_msg $ret
	;;
  restart|force-reload)
	capability_check
	case "$?" in
		0) ;;
		*) exit ;;
	esac
	log_daemon_msg "Restarting $DESC"
	ret=0
	qemu_restart
	case "$?" in
		0|1) ;;
		*) ret=1 ;;
	esac
	xenconsoled_restart
	case "$?" in
		0|1) ;;
		*) ret=1 ;;
	esac
	log_end_msg $ret
	;;
  *)
	echo "Usage: $0 {start|stop|restart|force-reload}" >&2
	exit 3
	;;
esac

exit 0
