#! /usr/lib/util-vserver/sigexec /bin/bash

# Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
#  
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#  
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#  
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

## Usage: vshelper <xid> <action> <args>

: ${UTIL_VSERVER_VARS:=/usr/lib/util-vserver/util-vserver-vars}
test -e "$UTIL_VSERVER_VARS" || {
    echo $"Can not find util-vserver installation (the file '$UTIL_VSERVER_VARS' would be expected); aborting..." >&2
    exit 1
}
. "$UTIL_VSERVER_VARS"
. "$_LIB_FUNCTIONS"

function showHelp
{
    echo $"\
Usage: vshelper <xid> <event> <args>*

Report bugs to <$PACKAGE_BUGREPORT>."
    exit 0
}

function showVersion
{
    echo $"\
vshelper $PACKAGE_VERSION -- userspace reboot helper
This program is part of $PACKAGE_STRING

Copyright (C) 2004 Enrico Scholz
This program is free software; you may redistribute it under the terms of
the GNU General Public License.  This program has absolutely no warranty."
    exit 0
}

function doInternalMethod
{
    local method=$1
    case "$method" in
	(restart)
	    case "$ACTION" in
		(restart)
		    logging $"Restarting vserver '$VSERVER'"

		    local sync_dir
		    vshelper.initStopSync sync_dir

		    spawn $_VSERVER --defaulttty "$VSERVER" restart &

		    disown %%
		    vshelper.waitForStopSync "$sync_dir"
		    ;;
		(halt|poweroff)
		    logging $"Stopping vserver '$VSERVER'"

		    local sync_dir
		    vshelper.initStopSync sync_dir

		    spawn $_VSERVER --defaulttty "$VSERVER" stop &

		    disown %%
		    vshelper.waitForStopSync "$sync_dir"
		    spawn killContext "$XID"
		    ;;
		(swsusp)
		    ## TODO: any senseful action here? Perhaps shutdown scheduler for it?
		    exit 0
		    ;;
		(restart2)	;;
		(shutdown)
		    logging $"Cleaning up after vserver '$VSERVER'"
		    spawn $_VSERVER --defaulttty "$VSERVER" stop
		    ;;
		(*)
		    warning $"Unknown action '$ACTION' for vserver '$VSERVER'"
		    exit 1
	    esac
	    ;;

	(async)
	    spawn killContext "$XID"
	    ;;
	    
	(sync)
	    local f=${METHOD_ARGS[0]}
	    test -n "$f" ||
		panic $"Insufficent arguments for method '$method' and vserver '$VSERVER'"

	    test -p "$f" ||
		panic $"File '$f' which is required for synchronisation of vserver '$VSERVER' is not a pipe"

	    spawn killContext "$XID"
	    echo "$ACTION" >"$f"
	    ;;

	(*)
	    local script
	    findObject -x script "$__CONFDIR"/.defaults/apps/vshelper-methods/"$method" "$__PKGLIBDIR"/vshelper-methods/"$method" ''
	    
	    test -n '$script' || {
		warning $"No handler for internal method '$method' found"
		exit 1
	    }

	    export VSERVER
	    execute "$script" "${ARGS[@]}"
    esac
}
    
function doDefaultMethod
{
    local handler

    vshelper.getHandler handler "$VSERVER" "$ACTION" || {
	warning $"No handler configured for action '$ACTION' on vserver '$VSERVER'"
	exit 1
    }

    case "$handler" in
	(/*)	execute "$handler" "${ARGS[@]}";;
	(:*)	doInternalMethod "${handler##:}" "$@";;
    esac
}

#===========

test "$1" != '--version' || showVersion
test "$1" != '--help'    || showHelp

if ! tty -s; then
    findObject -e _VS_LOGFILE "$__CONFDIR"/.defaults/apps/vshelper/logfile /dev/null
    findObject -e _VS_ERRFILE "$__CONFDIR"/.defaults/apps/vshelper/logfile /dev/null

    exec   </dev/null
    exec  >>$_VS_LOGFILE
    exec 2>>$_VS_ERRFILE
fi

logging "$(date): vshelper $*"
    
test "$#" -ge 2 ||
    panic $"vshelper called with missing arguments; try '--help' for more information"

    
vshelper.isEnabled || exit 0
! vshelper.isDebug || set -x
    
set -eu

if $_VSERVER_INFO - FEATURE vshelper0; then
    declare -r XID=$1
    declare -r ACTION=$2
elif $_VSERVER_INFO - FEATURE vshelper; then
    declare -r ACTION=$1
    declare -r XID=$2
else
    panic $"vshelper functionality not supported by kernel"
fi

findObject -x delegate "$__CONFDIR"/.defaults/apps/vshelper-delegate/"$ACTION" "$__PKGLIBDIR"/vshelper-delegate/"$ACTION" ''
test -e "$delegate" && exec -a "$ACTION" "$delegate" "$@"

this_xid=$($_VSERVER_INFO - XID)
pxid=
responsible_xid=$XID
while true; do
    pxid=$($_VSERVER_INFO "$responsible_xid" PXID) || break
    test "$pxid" -ne "$this_xid"                   || break
    responsible_xid=$pxid
done

vserver_id=$($_VSERVER_INFO "$responsible_xid" ID) || test "$ACTION" = "shutdown" ||
    panic $"No responsible vserver found for xid '$responsible_xid' ($XID); aborting..."
    
test "$XID" = "$responsible_xid" || {
    logging "Giving 'vshelper' task for '$XID' to parent vserver '$vserver_id' ($responsible_xid)"
    execute $_VSERVER "$vserver_id" exec $_VSHELPER "$@"
}

#===========

ARGS=( "$@" )

declare -a state
getFileArray state "$__VSHELPERSTATEDIR/$XID" && test ${#state[@]} -ge 2 || {
    logging "'vshelper' not configured for xid '$XID'"
    exit 0
}

declare -r VSERVER=${state[0]}

cfg_xid=$($_VSERVER_INFO "${state[0]}" CONTEXT false) ||
    panic $"Configured vserver '$VSERVER' does not seem to run; aborting..."

test "$cfg_xid" = "$XID" ||
    panic $"Configured ($cfg_xid) and actual ($XID) xid for vserver '$VSERVER' are mismatching; aborting..."

cur_xid=$($_VSERVER_INFO "$VSERVER" CONTEXT false) ||
    panic $"Vserver '$VSERVER' with '$XID' does not seem to run; strange..."

test "$cur_xid" = "$XID" ||
    panic $"Expected ($XID) and actual ($cur_xid) xid for vserver '$VSERVER' are mismatching; strange..."


method=${state[1]}
if test "${#state[@]}" -gt 2; then
    unset state[0] state[1]
    declare -ra METHOD_ARGS=( "${state[@]}" )
else
    declare -ra METHOD_ARGS=()
fi

case "$method" in
    (default)		doDefaultMethod "$@";;
    (sync|async)	doInternalMethod "$method" "$@";;
    (*)			panic $"Unknown method '$method' used by vserver '$VSERVER'";;
esac
