#!/bin/sh
#
# xendomains		Start / stop domains automatically when domain 0 boots / shuts down.
#
# chkconfig:		345 99 00
# description:		Start / stop Xen domains.
#
# This script offers fairly basic functionality.  It should work on Redhat
# but also on LSB-compliant SuSE releases and on Debian with the LSB package
# installed.  (LSB is the Linux Standard Base)
#
# Based on the example in the "Designing High Quality Integrated Linux
# Applications HOWTO" by Avi Alkalay
# <http://www.tldp.org/HOWTO/HighQuality-Apps-HOWTO/>
#
### BEGIN INIT INFO
# Provides:          xendomains
# Required-Start:    $syslog $remote_fs xenstored xenconsoled
# Should-Start:      xend
# Required-Stop:     $syslog $remote_fs xenstored xenconsoled
# Should-Stop:       xend
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start/stop secondary xen domains
# Description:       Start / stop domains automatically when domain 0 
#                    boots / shuts down.
### END INIT INFO

. /etc/rc.d/init.d/functions

CMD=xm
$CMD list >/dev/null 2>/dev/null || CMD=xl
$CMD list >/dev/null 2>/dev/null || exit 0

[ -e /proc/xen/privcmd ] || exit 0

if [ -r /etc/sysconfig/xendomains ]; then
	. /etc/sysconfig/xendomains
else
	echo "/etc/sysconfig/xendomains does not exist"
	if [ "$1" = "stop" ]; then
		exit 0
	else
		exit 6
	fi
fi

##
# Returns 0 (success) if the given parameter names a directory, and that
# directory is not empty.
#
contains_something() {
  if [ -d "$1" ] && [ `/bin/ls $1 | wc -l` -gt 0 ]; then
	return 0
  else
	return 1
  fi
}

# read name from xen config file
rdname() {
	NM=$($CMD create --quiet --dryrun --defconfig "$1" | sed -n 's/^.*(name \(.*\))$/\1/p')
}

rdnames() {
    NAMES=
    if ! contains_something "$XENDOMAINS_AUTO"; then 
	return
    fi
    for dom in $XENDOMAINS_AUTO/*; do
	rdname $dom
	if test -z $NAMES; then 
	    NAMES=$NM; 
	else
	    NAMES="$NAMES|$NM"
	fi
    done
}

parseln() {
    if [[ "$1" = "*(domain*" ]]; then
        name=;id=
    elif [[ "$1" = "*(name*" ]]; then
        name=$(echo $1 | sed -e 's/^.*(name \(.*\))$/\1/')
    elif [[ "$1" = "*(domid*" ]]; then
        id=$(echo $1 | sed -e 's/^.*(domid \(.*\))$/\1/')
    fi
    [ -n "$name" -a -n "$id" ] && return 0 || return 1
}

is_running() {
    rdname $1
    name=;id=
    $CMD list -l | grep '(\(domain\|domid\|name\)' | \
    while read LN; do
	parseln "$LN" || continue
	[ $id = 0 ] && continue
	case $name in 
	    ($NM)
		return 0
		;;
	esac
    done
    return 1
}

start() {
    if [ -f /var/lock/subsys/xendomains ]; then 
	echo -e "xendomains already running (lockfile exists)"
	return 
    fi

    saved_domains=" "
    if [ "$XENDOMAINS_RESTORE" = "true" ] && contains_something "$XENDOMAINS_SAVE"; then
	mkdir -p $(dirname "/var/lock/subsys/xendomains")
	touch /var/lock/subsys/xendomains
	echo -n "Restoring Xen domains:"
	saved_domains=`ls $XENDOMAINS_SAVE`
        for dom in $XENDOMAINS_SAVE/*; do
            if [ -f $dom ] ; then
                HEADER=`head -c 16 $dom | head -n 1 2> /dev/null`
                if [ $HEADER = "LinuxGuestRecord" ]; then
                    echo -n " ${dom##*/}"
                    XMR=`$CMD restore $dom 2>&1 1>/dev/null`
                    #$CMD restore $dom
                    if [ $? -ne 0 ]; then
                        echo -e "\nAn error occurred while restoring domain ${dom##*/}:\n$XMR"
                        echo -e '!'
                    else
                        # mv $dom ${dom%/*}/.${dom##*/}
                        rm $dom
                    fi
                fi
            fi
        done
	echo -e
    fi

    if contains_something "$XENDOMAINS_AUTO" ; then
	touch /var/lock/subsys/xendomains
	echo -n "Starting auto Xen domains:"
	# We expect config scripts for auto starting domains to be in
	# XENDOMAINS_AUTO - they could just be symlinks to files elsewhere

	# Create all domains with config files in XENDOMAINS_AUTO.
	# TODO: We should record which domain name belongs 
	# so we have the option to selectively shut down / migrate later
	# If a domain statefile from $XENDOMAINS_SAVE matches a domain name
	# in $XENDOMAINS_AUTO, do not try to start that domain; if it didn't 
	# restore correctly it requires administrative attention.
	for dom in $XENDOMAINS_AUTO/*; do
	    echo -n " ${dom##*/}"
	    shortdom=$(echo $dom | sed -n 's/^.*\/\(.*\)$/\1/p')
	    echo $saved_domains | grep -w $shortdom > /dev/null
	    if [ $? -eq 0 ] || is_running $dom; then
		echo -n "(skip)"
	    else
		XMC=`$CMD create --quiet --defconfig $dom`
		if [ $? -ne 0 ]; then
		    echo -e "\nAn error occurred while creating domain ${dom##*/}: $XMC\n"
		    echo -e '!'
		else
		    usleep $XENDOMAINS_CREATE_USLEEP
		fi
	    fi
	done
    fi
}

all_zombies() {
    name=;id=
    $CMD list -l | grep '(\(domain\|domid\|name\)' | \
    while read LN; do
	parseln "$LN" || continue
	if test $id = 0; then continue; fi
	if test "$state" != "-b---d" -a "$state" != "-----d"; then
	    return 1;
	fi
    done
    return 0
}

# Wait for max $XENDOMAINS_STOP_MAXWAIT for $CMD $1 to finish;
# if it has not exited by that time kill it, so the init script will
# succeed within a finite amount of time; if $2 is nonnull, it will
# kill the command as well as soon as no domain (except for zombies)
# are left (used for shutdown --all). Third parameter, if any, suppresses
# output of dots per working state (formatting issues)
watchdog_xencmd() {
    if test -z "$XENDOMAINS_STOP_MAXWAIT" -o "$XENDOMAINS_STOP_MAXWAIT" = "0"; then
	exit
    fi

    usleep 20000
    for no in `seq 0 $XENDOMAINS_STOP_MAXWAIT`; do
	# exit if $CMD save/migrate/shutdown is finished
	PSAX=`ps axlw | grep "$CMD $1" | grep -v grep`
	if test -z "$PSAX"; then exit; fi
	if ! test -n "$3"; then echo -n '.'; fi
	sleep 1
	# go to kill immediately if there's only zombies left
	if all_zombies && test -n "$2"; then break; fi
    done
    sleep 1
    PSPID=$($PSAX | awk '{ print $3 }')
    # kill $CMD $1
    kill $PSPID >/dev/null 2>&1
    
    echo -e .
}

stop() {
    # Collect list of domains to shut down
    if test "$XENDOMAINS_AUTO_ONLY" = "true"; then
	rdnames
    fi
    echo -n "Shutting down Xen domains:"
    name=;id=
    $CMD list -l | grep '(\(domain\|domid\|name\)' | \
    while read LN; do
	parseln "$LN" || continue
	if test $id = 0; then continue; fi
	echo -n " $name"
	if test "$XENDOMAINS_AUTO_ONLY" = "true"; then
	    eval "
	    case \"\$name\" in
		($NAMES)
		    # nothing
		    ;;
		(*)
		    echo -e '(skip)'
		    continue
		    ;;
	    esac
	    "
	fi
	# XENDOMAINS_SYSRQ chould be something like just "s" 
	# or "s e i u" or even "s e s i u o"
	# for the latter, you should set XENDOMAINS_USLEEP to 1200000 or so
	if test -n "$XENDOMAINS_SYSRQ"; then
	    for sysrq in $XENDOMAINS_SYSRQ; do
		echo -n "(SR-$sysrq)"
		XMR=`$CMD sysrq $id $sysrq 2>&1 1>/dev/null`
		if test $? -ne 0; then
		    echo -e "\nAn error occurred while doing sysrq on domain:\n$XMR\n"
		    echo -n '!'
		fi
		# usleep just ignores empty arg
		usleep $XENDOMAINS_USLEEP
	    done
	fi
	if test "$state" = "-b---d" -o "$state" = "-----d"; then
	    echo -n "(zomb)"
	    continue
	fi
	if test -n "$XENDOMAINS_MIGRATE"; then
	    echo -n "(migr)"
	    watchdog_xencmd migrate &
	    WDOG_PID=$!
	    XMR=`$CMD migrate $id $XENDOMAINS_MIGRATE 2>&1 1>/dev/null`
	    if test $? -ne 0; then
		echo -e "\nAn error occurred while migrating domain:\n$XMR\n"
		echo -e '!'

		kill $WDOG_PID >/dev/null 2>&1
	    else
		kill $WDOG_PID >/dev/null 2>&1
		
		echo -e .
		usleep 1000
		continue
	    fi
	fi
	if test -n "$XENDOMAINS_SAVE"; then
	    echo -n "(save)"
	    watchdog_xencmd save &
	    WDOG_PID=$!
	    mkdir -p "$XENDOMAINS_SAVE"
	    XMR=`$CMD save $id $XENDOMAINS_SAVE/$name 2>&1 1>/dev/null`
	    if test $? -ne 0; then
		echo -e "\nAn error occurred while saving domain:\n$XMR\n"
		echo -e '!'
		kill $WDOG_PID >/dev/null 2>&1
	    else
		kill $WDOG_PID >/dev/null 2>&1
		echo -e .
		usleep 1000
		continue
	    fi
	fi
	if test -n "$XENDOMAINS_SHUTDOWN"; then
	    # XENDOMAINS_SHUTDOWN should be "--halt --wait"
	    echo -n "(shut)"
	    watchdog_xencmd shutdown &
	    WDOG_PID=$!
	    XMR=`$CMD shutdown $id $XENDOMAINS_SHUTDOWN 2>&1 1>/dev/null`
	    if test $? -ne 0; then
		echo -e "\nAn error occurred while shutting down domain:\n$XMR\n"
		echo -e '!'
	    fi
	    kill $WDOG_PID >/dev/null 2>&1
	fi
    done

    # NB. this shuts down ALL Xen domains (politely), not just the ones in
    # AUTODIR/*
    # This is because it's easier to do ;-) but arguably if this script is run
    # on system shutdown then it's also the right thing to do.
    if ! all_zombies && test -n "$XENDOMAINS_SHUTDOWN_ALL"; then
	# XENDOMAINS_SHUTDOWN_ALL should be "--all --halt --wait"
	echo -n " SHUTDOWN_ALL "
	watchdog_xencmd shutdown 1 false &
	WDOG_PID=$!
	XMR=`$CMD shutdown $XENDOMAINS_SHUTDOWN_ALL 2>&1 1>/dev/null`
	if test $? -ne 0; then
	    echo -e "\nAn error occurred while shutting down all domains: $XMR\n"
	    echo -e '!'
	fi
	kill $WDOG_PID >/dev/null 2>&1
    fi

    # Unconditionally delete lock file
    rm -f /var/lock/subsys/xendomains
}

check_domain_up()
{
    name=;id=
    $CMD list -l | grep '(\(domain\|domid\|name\)' | \
    while read LN; do
	parseln "$LN" || continue
	if test $id = 0; then continue; fi
	case $name in 
	    ($1)
		return 0
		;;
	esac
    done
    return 1
}

check_all_auto_domains_up()
{
    if ! contains_something "$XENDOMAINS_AUTO"; then
      return 0
    fi
    missing=
    for nm in $XENDOMAINS_AUTO/*; do
	rdname $nm
	found=0
	if check_domain_up "$NM"; then 
	    echo -n " $name"
	else 
	    missing="$missing $NM"
	fi
    done
    if test -n "$missing"; then
	echo -n " MISS AUTO:$missing"
	return 1
    fi
    return 0
}

check_all_saved_domains_up()
{
    if ! contains_something "$XENDOMAINS_SAVE"; then
      return 0
    fi
    missing=`/bin/ls $XENDOMAINS_SAVE`
    echo -n " MISS SAVED: " $missing
    return 1
}

RETVAL=0
# See how we were called.
case "$1" in
    start)
	start
	;;
    stop)
	stop
	;;
    restart|reload)
# This does NOT necessarily restart all running domains: instead it
# stops all running domains and then boots all the domains specified in
# AUTODIR.  If other domains have been started manually then they will
# not get restarted.
    stop
    start
	;;
    status)
	if [ -f /var/lock/subsys/xendomains; then
	    echo -n "Checking for xendomains:" 
	    check_all_auto_domains_up
	    check_all_saved_domains_up
	fi
	;;
    *)
	msg_usage "$0 {start|stop|restart|reload|status}"
	;;
esac

exit $RETVAL
