#!/bin/bash

# Free implementation of nxserver components
#
# To use nxserver add the user "nx" 
# and use nxserver as default shell.
#
# Also make sure that hostkey based authentification works.
# 
# Copyright (c) 2004 by Fabian Franz <FreeNX@fabian-franz.de>.
#
# License: GNU GPL, version 2
#
# SVN: $Id: nxserver 580 2008-08-22 00:44:43Z fabianx $
#

# Read the config file
. $(PATH=$(cd $(dirname $0) && pwd):$PATH which nxloadconfig) --

# following two functions are Copyright by Klaus Knopper

stringinstring(){
case "$2" in *$1*) return 0;; esac
return 1
}

# Reread boot command line; echo last parameter's argument or return false.
getparam(){
stringinstring "&$1=" "$CMDLINE" || return 1
echo "$CMDLINE" |  tr "&" "\n" | egrep "^"$1"=" | awk -F= '{ VAL=$2 } END { print VAL }'
return 0
}

############### PACKAGE log.bm #######################
#
# Library of log functions (outsource)
#

# Loglevels:
# 1: Errors
# 2: Warnings
# 3: Important information
# 4: Server - Client communication
# 5: Information
# 6: Debugging information
# 7: stderror-channel of some applications

log()
{
	[ "$NX_LOG_LEVEL" -ge "$1" -a -w "$NX_LOGFILE" ] && shift && echo "$@" >> "$NX_LOGFILE"
}

# Log in a way that is secure for passwords / cookies / ...

echo_secure()
{
	echo "$@ " | $COMMAND_PERL -pi -e 's/--cookie=".+?"/--cookie="******"/g; s/--agent_password=".+?"/agent_password="******"/g; s/--password=".+?"/password="******"/g; s/cookie=.+?&/cookie=******&/g; s/agent_password=.+?&/agent_password=******&/g; s/password=.+?&/password=******&/g;'
}

log_secure()
{
	if [ "$NX_LOG_SECURE" = "0" ]
	then
		log "$@"
	else
		[ "$NX_LOG_LEVEL" -ge "$1" -a -w "$NX_LOGFILE" ] && shift && echo_secure "$@" >> "$NX_LOGFILE"
	fi
}

log_tee()
{
	[ "$NX_LOG_LEVEL" -ge "4" -a -w "$NX_LOGFILE" ] && exec tee -a "$NX_LOGFILE"
	[ "$NX_LOG_LEVEL" -ge "4" -a -w "$NX_LOGFILE" ] || exec cat -
}

log_error()
{
	[ "$NX_LOG_LEVEL" -ge "7" -a -w "$NX_LOGFILE" ] && exec tee -a "$NX_LOGFILE"
	[ "$NX_LOG_LEVEL" -ge "7" -a -w "$NX_LOGFILE" ] || exec cat - 
}

echo_x()
{
	log "4" "$@"
	echo "$@"
}




############### PACKAGE passdb.bm #######################
#
# Library of passdb functions (outsource)
#

# Policy: Variable and function names _must_ start with passdb_ / PASSDB_

# Needed global vars: $NX_ETC_DIR, $PATH_BIN

# Needed nonstd functions: md5sum


passdb_get_crypt_pass()
{
	echo "$@" | $COMMAND_MD5SUM | cut -d" " -f1
}

passdb_get_pass()
{
	PASSDB_CHUSER="$1"
	PASSDB_PASS=$(egrep "^$PASSDB_CHUSER:" $NX_ETC_DIR/passwords 2>/dev/null | cut -d":" -f2)
	if [ "$ENABLE_PASSDB_AUTHENTICATION" = "1" ]
	then
		egrep -q "^$PASSDB_CHUSER:" $NX_ETC_DIR/passwords 2>/dev/null && echo $PASSDB_PASS
		egrep -q "^$PASSDB_CHUSER:" $NX_ETC_DIR/passwords 2>/dev/null || echo "NOT_VALID"
	else
		echo "NOT_VALID"
	fi
}

passdb_chpass()
{
	PASSDB_CHUSER="$1"
	PASSDB_ENC_PASS="$2"
	cp -f $NX_ETC_DIR/passwords $NX_ETC_DIR/passwords.orig
	$COMMAND_PERL -pi -e "s/$PASSDB_CHUSER:.*/$PASSDB_CHUSER:$PASSDB_ENC_PASS/g" $NX_ETC_DIR/passwords
}

passdb_user_exists()
{
	PASSDB_CHUSER="$1"
	egrep -q "^$PASSDB_CHUSER:" $NX_ETC_DIR/passwords 2>/dev/null
}


passdb_remove_user()
{
	PASSDB_CHUSER="$1"
	cp -f $NX_ETC_DIR/passwords $NX_ETC_DIR/passwords.orig
	$COMMAND_PERL -pi -e "s/$PASSDB_CHUSER:.*\n//g" $NX_ETC_DIR/passwords
}

passdb_add_user()
{
	PASSDB_CHUSER="$1"
	cp -f $NX_ETC_DIR/passwords $NX_ETC_DIR/passwords.orig
	echo "$PASSDB_CHUSER:*" >> $NX_ETC_DIR/passwords
	# deactivated to avoid problems with comm-server
	su - $PASSDB_CHUSER -c "$PATH_BIN/nxnode --setkey"
}

passdb_list_user()
{
	cat $NX_ETC_DIR/passwords | cut -d":" -f1
}

#
# End of passdb Library
#

############### PACKAGE session.bm #######################
#
# Library of session management functions
#

# Needed global vars: $NX_SESS_DIR

session_list()
{
	cat $NX_SESS_DIR/running/sessionId"{$1}"
}

# Find all running session-filenames 

session_find_all()
{
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		echo $i
	done
}

# Find all running sessions of a id
session_find_id()
{
	[ -f $NX_SESS_DIR/running/sessionId"{$1}" ] && echo $NX_SESS_DIR/running/sessionId"{$1}"
}

# finds out if a session belongs to a user

session_find_id_user()
{
	[ -f $NX_SESS_DIR/running/sessionId"{$1}" ] && egrep -q "^userName=$2$" $NX_SESS_DIR/running/sessionId"{$1}" && return 0
	return 1
}

# Find all running sessions of a user
session_find_user()
{
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		egrep -q "^userName=$1$" $i && echo $i
	done
}

# Find all running sessions of a display
session_find_display()
{	
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		egrep -q "^display=$1$" $i && echo $i
	done
}

# session_get_cmdline <session filename>

session_get_cmdline()
{
	echo "a=b" | cat - $1 | tr '\n' '&'
}

# session_get <uniqueid>

session_get()
{
	session_get_cmdline $NX_SESS_DIR/running/sessionId"{$1}"
}


# Get the first session, which can be resumed

session_get_user_suspended()
{
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		if egrep -q "^userName=$1$" $i && egrep -q "^status=$2$" $i
		then
			CMDLINE=$(session_get_cmdline $i)
			echo "$(getparam sessionId)"
			break
		fi
	done
}

# Count all sessions of a user
# and save it in SESSION_COUNT and SESSION_COUNT_USER

session_count_user()
{
	SESSION_COUNT=0
	SESSION_COUNT_USER=0

	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		let SESSION_COUNT=$SESSION_COUNT+1
		egrep -q "^userName=$1$" $i && let SESSION_COUNT_USER=$SESSION_COUNT_USER+1
	done
}

# List all sessions of a user

session_list_user_suspended()
{
	SESSION_COUNT=0
	SESSION_COUNT_USER=0

	TMPFILE=$(mktemp /tmp/nxserver_tmp.XXXXXXXXX)
	echo "NX> 127 Sessions list of user '$1' for reconnect:" > $TMPFILE
	echo >> $TMPFILE
	if [ -z "$4" ]
	then
		
		echo "Display Type             Session ID                       Options  Depth Screensize     Available Session Name" >> $TMPFILE
		echo "------- ---------------- -------------------------------- -------- ----- -------------- --------- ----------------------" >> $TMPFILE
	else
		echo "Display Type             Session ID                       Options  Depth Screen         Status      Session Name" >> $TMPFILE
		echo "------- ---------------- -------------------------------- -------- ----- -------------- ----------- ------------------------------" >> $TMPFILE
	fi
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		let SESSION_COUNT=$SESSION_COUNT+1
		if egrep -q "^userName=$1$" $i && egrep -q "^status=$2$" $i #&& grep -q "screeninfo=$3" $i
		then
			CMDLINE=$(session_get_cmdline $i)
			
			if [ "$4" = "shadow" ]
			then
				if [ "$(getparam userName)" != "$USER" ]
				then 
					[ -z "$(getparam shadowcookie)" ] && continue
					
					if [ -x "$PATH_BIN/nxshadowacl" ]
					then
						$PATH_BIN/nxshadowacl "$(getparam userName)" "$USER" || continue
					fi
				fi
			fi
	
			depth=$(getparam screeninfo | cut -d "x" -f3 | cut -d "+" -f1 )
			[ "$depth" = "32" ] && depth=24
			geom=$(getparam screeninfo | cut -d "x" -f1,2) 
			render=$(getparam screeninfo | cut -d "+" -f2 )
			available="N/A"
			udepth=$(echo $3 | cut -d "x" -f3 | cut -d "+" -f1 )
			[ "$udepth" = "32" ] && udepth=24
			urender=$(echo $3 | cut -d "+" -f2 )

			mode="D"
			[ "$(getparam sessionRootlessMode)" = "1" ] && mode="-"
			
			options="-"
			stringinstring "fullscreen" "$3" && options="F"
			[ "$(getparam geometry)" = "fullscreen" ] || options="-"
			[ "$urender" = "render" ] && options="${options}R${mode}--PSA"
			[ "$urender" = "render" ] || options="${options}-${mode}--PSA"
			[ "$udepth" = "$depth" -a "$urender" = "$render" ] && available=$(getparam status)
			# FIXME: HACK !!! to keep compatibility with old snapshot version (Knoppix 3.6 based for example)
			if [ -z "$4" -a "$available" != "N/A" ] 
			then
				available="Yes"
			fi
			
			# We automatically offer VNC shadowed sessions for "remote" support
			if [ "$4" = "vnc" -a "$ENABLE_MIRROR_VIA_VNC" = "1" ] && stringinstring "unix-" "$(getparam type)"
			then
				available=$(getparam status)
				printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$(getparam display)" "vnc-shadowed" "$(getparam sessionId)" "$options" "$depth" "$geom" "$available" "$(getparam sessionName) (Mirrored)" >> $TMPFILE
			elif [ "$4" = "shadow" ]
			then
				available=$(getparam status)
				printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$(getparam display)" "$(getparam type)" "$(getparam sessionId)" "$options" "$depth" "$geom" "$available" "$(getparam sessionName) (Shadowed)" >> $TMPFILE
			else
				# only unix-* sessions can be resumed, but other session types can still be terminated
				stringinstring "unix-" "$4" || available="N/A"
				printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$(getparam display)" "$(getparam type)" "$(getparam sessionId)" "$options" "$depth" "$geom" "$available" "$(getparam sessionName)" >> $TMPFILE
			fi
		fi
		egrep -q "^userName=$1$" $i && let SESSION_COUNT_USER=$SESSION_COUNT_USER+1
	done
	
	if [ "$4" = "vnc" -a "$ENABLE_DESKTOP_SHARING" = "1" ]
	then
		export DESKTOP_SHARING_IDS=""
		for i in $(LC_ALL=C netstat -ln --protocol=unix | egrep 'X11-unix/X[0-9]$' | sed 's/.*X\(.*\)/\1/g')
		do
			uniqueid=$(echo $[$RANDOM*$RANDOM] | $COMMAND_MD5SUM | cut -d" " -f1 | tr "[a-z]" "[A-Z]")
			DESKTOP_SHARING_IDS="$DESKTOP_SHARING_IDS $uniqueid=$i"
			printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$i" "vnc-local" "$uniqueid" "--------" "$udepth" "$(echo $3 | cut -d'x' -f1,2)" "Running" "X$i (Local)" >> $TMPFILE
		done
	fi
	
	echo "" >> $TMPFILE
	echo "" >> $TMPFILE
	
	if [ "$SESSION_COUNT" -ge "$SESSION_LIMIT" -o "$SESSION_COUNT_USER" -ge "$SESSION_USER_LIMIT" ]
	then
		echo "NX> 147 Server capacity: reached for user: $1" >> $TMPFILE
	else
		echo "NX> 148 Server capacity: not reached for user: $1" >> $TMPFILE
	fi
	
	cat $TMPFILE | log_tee
	rm -f $TMPFILE
}

session_list_user()
{
	echo "NX> 127 Sessions list of user '$1'"
	echo
	echo "Server     Display Username        Remote IP       Session ID"
	echo "------ ------- --------------- --------------- --------------------------------"
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		if egrep -q "^userName=$1$" $i
		then
			CMDLINE=$(session_get_cmdline $i)
			echo -e "$(getparam host)\t$(getparam display)\t$(getparam userName)\t$(getparam foreignAddress)\t$(getparam sessionId)"
		fi
	done
}

session_history()
{
	userName=$1
	sessionId=$2
	echo "NX> 127 Session list:"
	echo
	echo "Display Username        Remote IP       Session ID                       Date                Status"
	echo "------- --------------- --------------- -------------------------------- ------------------- -----------"
	for j in $(ls --time-style +%s -la "$NX_SESS_DIR"/{closed,failed,running} | awk '/sessionId/ { print $6 " " $7 }' | sort -n | cut -d" " -f2)
	do
		if [ -n "$sessionId" ]
		then
			[ "$j" = "sessionId{$sessionId}" ] || continue
		fi
		i="$NX_SESS_DIR"/*/"$j"
		[ -f $i ] || break
		CMDLINE=$(session_get_cmdline $i)
		if [ -n "$userName" ]
		then
			[ "$userName" = "$(getparam userName)" ] || continue
		fi
		echo -e "$(getparam display)\t$(getparam userName)\t$(getparam foreignAddress)\t$(getparam sessionId)\t$(ls --time-style="+%F %X" -l $i | awk '/sessionId/ { print $6 " " $7 }')\t$(getparam status)"
	done
}

# remove all sessions older than $SESSION_HISTORY seconds in failed/closed.

session_cleanup()
{
	[ "$SESSION_HISTORY" -gt "-1" ] || return
	let SESSION_HISTORY_MINUTES=$SESSION_HISTORY/60

	# If you need posix find compatibility, use the outcommented variant below.
	find $NX_SESS_DIR/closed/ $NX_SESS_DIR/failed/ -type f -mmin +"$SESSION_HISTORY_MINUTES" -exec rm -f '{}' ';'
	#let SESSION_HISTORY_DAYS=$SESSION_HISTORY_MINUTES/1440
	#find $NX_SESS_DIR/closed/ $NX_SESS_DIR/failed/ -type f -mtime +"$SESSION_HISTORY_DAYS" -exec rm -f '{}' ';'
}

session_list_all()
{
	echo "NX> 127 Sessions list:"
	echo
	echo "Server     Display Username        Remote IP       Session ID"
	echo "------ ------- --------------- --------------- --------------------------------"
	for i in $NX_SESS_DIR/running/*
	do
		[ -f $i ] || break
		CMDLINE=$(session_get_cmdline $i)
		echo -e "$(getparam host)\t$(getparam display)\t$(getparam userName)\t$(getparam foreignAddress)\t$(getparam sessionId)"
	done
}


# session_add <session_id> <options>

session_add()
{
	id=$1
	shift
	echo "$@" | tr '&' '\n' > $NX_SESS_DIR/running/sessionId'{'$id'}'
}

# session_change <session_id> <parameter> <new_value>

session_change()
{
	[ -f $NX_SESS_DIR/running/sessionId'{'$1'}' ] && $COMMAND_PERL -pi -e "s/$2=.*/$2=$3/" $NX_SESS_DIR/running/sessionId'{'$1'}'
}

# session_id <new status>

session_status()
{
	session_change "$1" "status" "$2"
}

# session_running <session_id>
# return: true if running, false if not

session_running()
{
	test -f $NX_SESS_DIR/running/sessionId'{'$1'}'
}

# session_close <session_id> <end-time>

session_close()
{
	$COMMAND_PERL -pi -e "s/startTime=\(.*\)/startTime=\1\nendTime=$(date +%s)/" $NX_SESS_DIR/running/sessionId'{'$1'}'
	session_status $1 "Finished"
	[ "$SESSION_HISTORY" = "0" ] && rm -f $NX_SESS_DIR/running/sessionId'{'$1'}'
	[ "$SESSION_HISTORY" = "0" ] || mv -f $NX_SESS_DIR/running/sessionId'{'$1'}' $NX_SESS_DIR/closed/sessionId'{'$1'}'
}

session_fail()
{
	$COMMAND_PERL -pi -e "s/startTime=\(.*\)/startTime=\1\nendTime=$(date +%s)/" $NX_SESS_DIR/running/sessionId'{'$1'}'
	session_status $1 "Failed"
	[ "$SESSION_HISTORY" = "0" ] && rm -f $NX_SESS_DIR/running/sessionId'{'$1'}'
	[ "$SESSION_HISTORY" = "0" ] || mv -f $NX_SESS_DIR/running/sessionId'{'$1'}' $NX_SESS_DIR/failed/sessionId'{'$1'}'
}

session_suspend()
{
	session_status $1 "Suspended"
	session_change $1 foreignAddress "-"
}

#
# end of library
#


#
# Main nxserver <-> nxclient communication module
#

# do not run in server mode loop
SERVER_MODE="0"

# For users nxfree and nx run in SERVER MODE
[ "$USER" = "nxfree" -o "$USER" = "nx" ] && SERVER_MODE="1"

# We can override usermode via environment var
[ "$NX_USERMODE" = "1" ] && ENABLE_USERMODE_AUTHENTICATION="1"

# When usermode is 1, we only run in server loop
# if nxserver was run without any arguments or just with -c nxserver.
if [ "$ENABLE_USERMODE_AUTHENTICATION" = "1" ]
then 
	# do not run in server mode loop regardless of username
	SERVER_MODE="0"

	[ -z "$*" -o "$*" = "-c $PATH_BIN/nxserver" ] && SERVER_MODE="1"
	
	# Reread the config files (so that $USER.node.conf get sourced)
	. $(PATH=$(cd $(dirname $0) && pwd):$PATH which nxloadconfig) --userconf
	
	# We might need to reactivate this now
	ENABLE_USERMODE_AUTHENTICATION="1"
	
	# export the necessary variables
	export NX_SESS_DIR="$USER_FAKE_HOME/.nx/db/"
	export NX_LOGFILE="$USER_FAKE_HOME/.nx/temp/nxserver.log"
	mkdir -p $(dirname $NX_LOGFILE)
	mkdir -p $NX_SESS_DIR/{closed,running,failed}
	
	# we are logged in already 
	LOGIN_SUCCESS="1"
	LOGIN_METHOD="USERMODE"

	# we do not use SLAVE mode for usermode
	ENABLE_SLAVE_MODE="0"
	# we do not use loadbalancing mode for usermode
	LOAD_BALANCE_SERVERS=""
fi

if [ "$SERVER_MODE" = "1" ]
then

#
# needed for slave mode
#

nxnode_login_stop_slave()
{
	if [ -n "$NXNODE_LOGIN_SLAVE" ]
	then
		log 3 "Info: Closing connection to slave with pid $NXNODE_LOGIN_SLAVE."
		# send quit command
		echo "--quit" >&$NX_COMMFD

		# kill process
		kill "$NXNODE_LOGIN_SLAVE"
		sleep 2
		kill -0 "$NXNODE_LOGIN_SLAVE" && kill -9 "$NXNODE_LOGIN_SLAVE"
		unset NXNODE_LOGIN_SLAVE
	fi
}

nxnode_login()
{
	PASS="$1"
	shift

	if [ "$NXNODE_LOGIN_SLAVE_ENABLED" != "1" ]
	then
		NXNODE_TOSEND="$NXNODE_TOSEND" echo $PASS | $PATH_BIN/nxnode-login "$@"
	else
		if [ -z "$NXNODE_LOGIN_SLAVE" -a -z "$NX_TRUSTED_USER" ]
		then
			# Send password
			echo "$PASS" >&$NX_COMMFD

			# Connect to NXNODE
			
			( $PATH_BIN/nxnode-login "$1" "$2" "$3" "$4" "$5" "--slave" <&$NX_SERVERFD >&$NX_SERVERFD 2>&$NX_SERVERFD || echo "FREENX> 716 Slave mode failed to start." >&$NX_SERVERFD )  &
			NXNODE_LOGIN_SLAVE="$!"
			disown $!

			trap nxnode_login_stop_slave EXIT

			NXNODE_SLAVE_STARTED=""
			
			# FIXME: Make timeout configurable
			while read -t 10 line <&$NX_COMMFD
			do
				log 6 "$line"
				case "$line" in
					"NX> 716 Slave mode started successfully.")
						NXNODE_SLAVE_STARTED="1"
						break
					;;
					"FREENX> 716 Slave mode failed to start.")
						break
					;;
				esac
			done

			if [ -z "$NXNODE_SLAVE_STARTED" ]
			then
				# stop it, if it still exists
				nxnode_login_stop_slave 
				unset NXNODE_LOGIN_SLAVE
				# return an error
				return 1
			fi
		fi
		
		#send CMD to nxnode
		
		echo "$6" >&$NX_COMMFD
		[ -n "$NXNODE_TOSEND" ] && echo "$NXNODE_TOSEND" >&$NX_COMMFD

		NXNODE_RETURN="1"

		if [ -z "$NXNODE_READER" ]
		then
			while read line <&$NX_COMMFD
			do
				log 6 "nxnode_reader: $line"
				echo "$line"
				case "$line" in
					"NX> 716"*) 
						NXNODE_RETURN="0"
					;;
					"NX> 1001"*)
						break
					;;
				esac
			done
		fi
		test "$NXNODE_RETURN" = "0"
	fi
}

nxnode_login_register_reader()
{
	# Register a reader
	NXNODE_READER="1"
}

# Start!
[ "$NX_LOG_LEVEL" -ge "1" ] && touch "$NX_LOGFILE" >/dev/null 2>&1
log 3 "-- NX SERVER START: $@ - ORIG_COMMAND=$SSH_ORIGINAL_COMMAND"

if [ "$ENABLE_SERVER_FORWARD" = "1" -a -n "$SERVER_FORWARD_HOST" ]
then
	log 3 "Info: Forwarding connection to $SERVER_FORWARD_HOST with secret key $SERVER_FORWARD_KEY."
	$COMMAND_SSH -i "$SERVER_FORWARD_KEY" "-p$SERVER_FORWARD_PORT" "nx@$SERVER_FORWARD_HOST" "host=$SERVER_NAME"
	exit 0
fi

# Get the hostname out of SSH_ORIGINAL_COMMAND
PREFERRED_HOST=$(echo $SSH_ORIGINAL_COMMAND | tr '&' '\n' | grep "^host=" | cut -d'=' -f2)

# forward the connection to commercial NoMachine server?
if [ "$ENABLE_NOMACHINE_FORWARD_PORT" = "1" -a "$NOMACHINE_FORWARD_PORT" = "$(echo $SSH_CLIENT $SSH2_CLIENT| cut -d' ' -f3)" -a -n "$NOMACHINE_SERVER" ]
then
	log 3 "Info: Detected SSH destination port $NOMACHINE_FORWARD_PORT. Forwarding connection to commercial NoMachine server."
	exec $NOMACHINE_SERVER
	log 1 "Error: Forwarding to NoMachine Server $NOMACHINE_SERVER failed. Using FreeNX server instead."
fi

#
# nxnode slave mode preparations
#

NXNODE_LOGIN_SLAVE_ENABLED="0"
NXNODE_LOGIN_SLAVE=""

# slave mode does not work with load balancing
if [ -n "$LOAD_BALANCE_SERVERS" -a "$ENABLE_SLAVE_MODE" = "1" ]
then
	log 2 "Warning: Disabled slave mode. It does not work together with load balancing."
	ENABLE_SLAVE_MODE="0"
fi

if [ "$ENABLE_SLAVE_MODE" = "1" -a "$NXSERVER_HELPER_ACTIVE" != "1" -a -z "$NX_TRUSTED_USER" ]
then
	export SSH_ORIGINAL_COMMAND
	export NXSERVER_HELPER_ACTIVE="1"
	exec $PATH_BIN/nxserver-helper "$0"
	log 1 "Error: Execution of $PATH_BIN/nxserver-helper failed. Disabling slave mode of nxnode."
	export NXSERVER_HELPER_ACTIVE="0"
fi

if [ "$ENABLE_SLAVE_MODE" = "1" -a "$NXSERVER_HELPER_ACTIVE" = "1" ]
then
	log 3 "Info: Using fds #$NX_SERVERFD and #$NX_COMMFD for communication with nxnode."
	NXNODE_LOGIN_SLAVE_ENABLED="1"
fi

if [ -n "$NX_TRUSTED_USER" -a -n "$NX_COMMFD" ]
then
	NXNODE_LOGIN_SLAVE_ENABLED="1"
	USER=$NX_TRUSTED_USER
	
	# check that communication works
	nxnode_login "" -- su "$USER" "" "$PATH_BIN/nxnode" --check 2>&1 >/dev/null

	if [ $? -ne 0 ]
	then
		log 1 "Error: Could not establish communication with trusted user ($USER) nxnode."
		echo_x "NX> 404 ERROR: wrong password or login"
		echo_x "NX> 999 Bye"
		exit 1
	fi
	
	LOGIN_SUCCESS="1"
	LOGIN_METHOD="SU"

	log 3 "Info: Using fd #$NX_COMMFD for communication with trusted user ($USER) established nxnode."
	
	# Reread the config files (so that $USER.node.conf get sourced)
	. $(PATH=$(cd $(dirname $0) && pwd):$PATH which nxloadconfig) --userconf
elif [ "$NX_USERMODE" = "1" ]
then
	log 3 "Info: Usermode authentication: Logged in as $USER."
else

echo_x "HELLO NXSERVER - Version $NX_VERSION $NX_LICENSE"

# Login stage
while true
do
	echo_x -n "NX> 105 "
	read CMD
	# FIXME?
	[ "$CMD" = "" ] && CMD="quit"
	echo_x "$CMD"
	
	case "$CMD" in 
		quit|QUIT)
			echo_x "Quit"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		exit|EXIT)
			echo_x "Exit"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		bye|BYE)
			echo_x "Bye"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		hello*|HELLO*)
			PROTO=$(echo $CMD | sed 's/.*Version \(.*\)/\1/g')
			echo_x "NX> 134 Accepted protocol: $PROTO"
			if [ "$PROTO" = "1.3.0" -o "$PROTO" = "1.3.2" ]
			then
				[ "$ENABLE_AUTORECONNECT_BEFORE_140" = "1" ] && ENABLE_AUTORECONNECT="1"
			fi
		;;
		"set auth_mode*"|"SET AUTH_MODE*")
			if [ "$CMD" = "set auth_mode password" -o "$CMD" = "SET AUTH_MODE PASSWORD" ]
			then
				echo_x "Set auth_mode: password"
			else
				echo_x "NX> 500 ERROR: unknown auth mode ''"
			fi
		;;
		login|LOGIN)
			LOGIN_SUCCESS="0"
			
			echo_x -n "NX> 101 User: "
			read USER2
			echo_x $USER2
			
			echo_x -n "NX> 102 Password: "
			old_ifs="$IFS"
			export IFS=$'\n'
			read -r -s PASS
			export IFS="$old_ifs"
			echo_x ""
			log 6 -n "Info: Auth method: "
			
			# USER already logged in?
			if [ "$ENABLE_USERMODE_AUTHENTICATION" = "1" ]
			then
				LOGIN_SUCCESS="1"
				# we have read the user config already above,
				# so we don't get any new information here
				break
			fi

			USER=$USER2

			# PASSDB based auth
			if [ "$ENABLE_PASSDB_AUTHENTICATION" = "1" -a "$LOGIN_SUCCESS" = "0" ]
			then
				log 6 -n "passdb "
				if [ $(passdb_get_crypt_pass "$PASS") = $(passdb_get_pass "$USER") ]
				then
					LOGIN_SUCCESS="1"
					LOGIN_METHOD="PASSDB"
				fi
			fi

			# SSH based auth
			if [ "$ENABLE_SSH_AUTHENTICATION" = "1" -a "$LOGIN_SUCCESS" = "0" ]
			then
				log 6 -n "ssh "
				export COMMAND_SSH			
				nxnode_login "$PASS" -- ssh "$USER" "$SSHD_PORT" "$PATH_BIN/nxnode" --check 2>&1 >/dev/null
				if [ $? -eq 0 ]
				then
					LOGIN_SUCCESS="1"
					LOGIN_METHOD="SSH"
				fi
			fi
			
			# SU based auth
			if [ "$ENABLE_SU_AUTHENTICATION" = "1" -a "$LOGIN_SUCCESS" = "0" ]
			then
				log 6 -n "su "
				nxnode_login "$PASS" -- su "$USER" "" "$PATH_BIN/nxnode" --check 2>&1 >/dev/null
				if [ $? -eq 0 ]
				then
					LOGIN_SUCCESS="1"
					LOGIN_METHOD="SU"
				fi
			fi
			
			# Check if user in passdb
			if [ "$ENABLE_USER_DB" = "1" ]
			then
				log 6 "userdb check"
				passdb_user_exists "$USER" || LOGIN_SUCCESS="0"
			fi
			log 6 ""

			if [ "$LOGIN_SUCCESS" = "1" ]
			then
				# Reread the config files (so that $USER.node.conf get sourced)
				. $(PATH=$(cd $(dirname $0) && pwd):$PATH which nxloadconfig) --userconf
				break
			else
				echo_x "NX> 404 ERROR: wrong password or login"
				echo_x "NX> 999 Bye"
				if [ "$ENABLE_LOG_FAILED_LOGINS" = "1" ]
				then
					logger -t nxserver -i -p auth.info "($(whoami)) Failed login for user=$USER from IP=$(echo $SSH_CLIENT | awk '{print $1}')"
				fi
				exit 1
			fi
		;;
	esac
done

fi

echo_x "NX> 103 Welcome to: $SERVER_NAME user: $USER"

# Add the slave mode shutdown trap (just in case)
[ -n "$NXNODE_LOGIN_SLAVE" ] && trap nxnode_login_stop_slave EXIT

# remove old session infos from history
session_cleanup

#
# call it with: server_get_params $CMD # no ""!
#

server_get_params()
{
	SERVER_PARAMS=$(echo "$@" | sed "s/^$1/\"/g; s/\" --/\&/g; s/\"//g; s/%20/ /g")
	if [ "$SERVER_PARAMS" = "" ]
	then
		echo_x -n "NX> 106 Parameters: "
		read SERVER_PARAMS2
		SERVER_PARAMS=$(echo $SERVER_PARAMS2 | sed 's/%2B/+/g; s/%20/ /g')
		echo_x
	fi
}

nxnode_start()
{
	:
	#CMD="$1"
	#shift
	#echo "$@" | $PATH_BIN/nxnode "$CMD"
}

#NX> 1002 Commit
#NX> 1006 Session status: running

server_nxnode_start()
{
	CMD="$1"
	USER="$2"
	shift
	shift

	# Find NODE_HOSTNAME
	
	NODE_HOSTNAME=""
	CMDLINE="$@"
	uniqueid=$(getparam uniqueid)
	[ -z "$uniqueid" ] && uniqueid=$(getparam sessionid)
	[ -z "$uniqueid" ] && uniqueid=$(getparam session_id)
	CMDLINE=$(session_get "$uniqueid")
	
	NODE_HOSTNAME="$(getparam host)"
	[ -z "$NODE_HOSTNAME" ] && NODE_HOSTNAME="127.0.0.1"
	export NODE_HOSTNAME
	
	# Use nxnode-login?
	if [ "$LOGIN_METHOD" = "SSH" ]
	then
	    export COMMAND_SSH
	    NXNODE_TOSEND="$@" nxnode_login "$PASS" -- ssh "$USER" "$SSHD_PORT" "$PATH_BIN/nxnode" "$CMD" 2>&1 | log_tee
	elif [ "$LOGIN_METHOD" = "SU" ]
	then
	    NXNODE_TOSEND="$@" nxnode_login "$PASS" -- su "$USER" "" "$PATH_BIN/nxnode" "$CMD" 2>&1 | log_tee
	elif [ "$LOGIN_METHOD" = "USERMODE" ]
	then
	    # we need to unset SSH_* variables so that nxnode 
	    # chooses the right accept= value.
	    unset SSH_CLIENT SSH_CLIENT2
	    echo "$@" | $PATH_BIN/nxnode "$CMD" 2>&1 | log_tee
	else
	    echo "$@" | $COMMAND_SSH -l "$USER" "$NODE_HOSTNAME" -p $SSHD_PORT -x -2 -i $NX_ETC_DIR/users.id_dsa -o 'PubkeyAuthentication yes' -o 'RSAAuthentication yes' -o 'RhostsAuthentication no' -o 'PasswordAuthentication no' -o 'RhostsRSAAuthentication no' -o 'StrictHostKeyChecking no' $PATH_BIN/nxnode "$CMD" | log_tee
	fi
}

server_add_usession()
{
	[ "$ENABLE_USESSION" = "1" ] || return
	
	$COMMAND_SESSREG -l ":$SESS_DISPLAY" -h "$USERIP" -a $USER 2>&1 | log_error
}

server_remove_usession()
{
	[ "$ENABLE_USESSION" = "1" ] || return
	$COMMAND_SESSREG -l ":$SESS_DISPLAY" -h "$USERIP" -d $USER 2>&1 | log_error
}

server_nxnode_echo()
{
	log 6 "server_nxnode_echo: $@"
	[ "$SERVER_CHANNEL" = "1" ] && echo "$@"
	[ "$SERVER_CHANNEL" = "2" ] && echo "$@" >&2
}

server_nxnode_exit_func()
{
	log 1 "Info: Emergency-Shutting down due to kill signal ..."
	
	session_fail $uniqueid
	
	server_remove_usession

	# remove lock file
	[ -e "/tmp/.nX$SESS_DISPLAY-lock" ] && rm -f /tmp/.nX$SESS_DISPLAY-lock

	# Kill possible slave node
	nxnode_login_stop_slave
}

server_nxnode_start_wait()
{
	if [ "$1" = "--startsession" ]
	then
	
	server_add_usession

	# We need to stop sending things when a SIGPIPE arrives
	trap "SERVER_CHANNEL=0" SIGPIPE
	
	trap server_nxnode_exit_func EXIT
	
	SERVER_CHANNEL=1
	KILL_WAIT_PID=1
	server_nxnode_start "$@" | while read CMD
	do
		case "$CMD" in 
			"NX> 706"*)
				if [ -x "$PATH_BIN/nxshadowacl" ]
				then
					# check if we should save the cookie
					$PATH_BIN/nxshadowacl "$USER"
					
					if [ $? -eq 0 ]
					then
						SHADOW_COOKIE=$(echo $CMD | cut -d: -f2 | sed 's/ //g')
						session_change "$uniqueid" "shadowcookie" "$SHADOW_COOKIE"
					fi
				fi
			;;
			"NX> 1006"*|"NX> 1005"*|"NX> 1009"*)
				case "$CMD" in 
					*running*)
						[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
						KILL_WAIT_PID=0
						log 6 session_status $uniqueid "Running"
						session_status $uniqueid "Running"
						[ "$SERVER_CHANNEL" = "1" ] && SERVER_CHANNEL=2
					;;
					*closed*)
						log 6 session_close $uniqueid
						session_close $uniqueid
					;;
					*suspended*)
						[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
						KILL_WAIT_PID=0
						log 6 session_suspend $uniqueid
						session_suspend $uniqueid
					;;
					*suspending*)
						log 6 session_status $uniqueid "Suspending"
						session_status $uniqueid "Suspending"
						# we need to stop sending to client as it will have already
						# closed his side of the channel and this will lead to not 
						# closed sessions.
						SERVER_CHANNEL=0
					;;
					*terminating*)
						log 6 session_status $uniqueid "Terminating"
						session_status $uniqueid "Terminating"
						# we need to stop sending to client as it will have already
						# closed his side of the channel and this will lead to not 
						# closed sessions.
						SERVER_CHANNEL=0
					;;
				esac
			;;
			"NX> 1004"*)
				[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
				KILL_WAIT_PID=0
				session_fail $uniqueid
				server_nxnode_echo "NX> 596 Session startup failed."
				log 4 "NX> 596 Session startup failed."
			;;
		esac

		case $CMD in
			"NX> "*)
				server_nxnode_echo $CMD
			;;
		esac
	done

	trap - EXIT
	trap - SIGPIPE
	
	# Close it in case the session is still running
	session_running $uniqueid && session_close $uniqueid
	
	server_remove_usession

	# remove lock file
	[ -e "/tmp/.nX$SESS_DISPLAY-lock" ] && rm -f /tmp/.nX$SESS_DISPLAY-lock

	nxnode_login_stop_slave
	
	# $1 = restore
	else
	
	KILL_WAIT_PID=1
	SERVER_CHANNEL=1
	server_nxnode_start "$@" | while read CMD
	do
		case "$CMD" in 
			"NX> 706"*)
				if [ -x "$PATH_BIN/nxshadowacl" ]
				then
					# check if we should save the cookie
					$PATH_BIN/nxshadowacl "$USER"
					
					if [ $? -eq 0 ]
					then
						SHADOW_COOKIE=$(echo $CMD | cut -d: -f2 | sed 's/ //g')
						session_change "$uniqueid" "shadowcookie" "$SHADOW_COOKIE"
					fi
				fi
			;;
			"NX> 1006"*|"NX> 1005"*|"NX> 1009"*)
				case "$CMD" in 
					*running*)
						[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
						KILL_WAIT_PID=0
						SERVER_CHANNEL=2
					;;
				esac
			;;
			"NX> 1004"*)
				[ "$KILL_WAIT_PID" = "1" ] && kill $SERVER_WAIT_PID
				KILL_WAIT_PID=0
				
				# This fail is correct here as somehow the 
				# monitor process might have died and we don't 
				# want the session to be resumed again.
				
				session_fail $uniqueid
				
				server_nxnode_echo "NX> 596 Session startup failed."
				log 4 "NX> 596 Session startup failed."
				break;
			;;
		esac
	
		case $CMD in
			"NX> "*)
				server_nxnode_echo $CMD
			;;
		esac
	done
	
	nxnode_login_stop_slave
	
	# $1 = start
	fi
}

server_check_session_count()
{
	session_count_user "$USER"
	
	if [ "$SESSION_COUNT" -ge "$SESSION_LIMIT" ]
	then
		echo_x "NX> 599 Reached the maximum number of concurrent sessions on this server."
		echo_x "NX> 500 ERROR: Last operation failed."
		return 1
	fi
	
	if [ "$SESSION_COUNT_USER" -ge "$SESSION_USER_LIMIT" ]
	then
		echo_x "NX> 599 Server capacity: reached for user: $USER"
		echo_x "NX> 500 ERROR: Last operation failed."
		return 1
	fi

	return 0
}

server_loadbalance_random()
{
	# Pick one based on "random" algorithm
	SERVER_LB_HOSTS=( $LOAD_BALANCE_SERVERS )
	SERVER_LB_NR_OF_HOSTS=${#SERVER_LB_HOSTS[@]}
	let SERVER_LB_NR=(RANDOM % SERVER_LB_NR_OF_HOSTS)
	SERVER_LB_HOST=${SERVER_LB_HOSTS[$SERVER_LB_NR]}
	echo $SERVER_LB_HOST
}

# run in subshell!

server_loadbalance_round_robin()
{
	SERVER_LB_HOSTS=( $LOAD_BALANCE_SERVERS )
	SERVER_LB_NR_OF_HOSTS=${#SERVER_LB_HOSTS[@]}
	
	# Atomic incrementation:

	# Enter critical section
	# - Create .lock file
	
	SERVER_LB_LOCKFILE=$(mktemp "$NX_SESS_DIR/round-robin.lock.XXXXXXXXX")

	trap "rm -f $SERVER_LB_LOCKFILE" EXIT
	
	i=0
	while [ $i -lt 200 ]
	do
		# ln is an atomic operation
		ln $SERVER_LB_LOCKFILE "$NX_SESS_DIR/round-robin.lock" && break
		LANG=C sleep 0.01
		let i=i+1
	done

	if [ $i -ge 200 ]
	then
		log 1 "Load-Balancing: Round-Robin failed to gain lock file in 200 tries. Falling back to random."
		server_loadbalance_random
		return
	fi
	
	trap "rm -f \"$SERVER_LB_LOCKFILE\" \"$NX_SESS_DIR/round-robin.lock\"" EXIT

	# Lock held

	SERVER_LB_NR=$(cat $NX_SESS_DIR/round-robin 2>/dev/null)
	let SERVER_LB_NR=(SERVER_LB_NR+1) % SERVER_LB_NR_OF_HOSTS
	echo $SERVER_LB_NR >$NX_SESS_DIR/round-robin

	# Exit critical section
	rm -f "$SERVER_LB_LOCKFILE" "$NX_SESS_DIR/round-robin.lock"

	trap - EXIT

	SERVER_LB_HOST=${SERVER_LB_HOSTS[$SERVER_LB_NR]}
	echo $SERVER_LB_HOST
}

server_loadbalance_load()
{
	SERVER_LB_MAX=0
	SERVER_LB_HOST=""
	
	for i in $LOAD_BALANCE_SERVERS
	do
		SERVER_LB_LOAD=$($NX_DIR/bin/nxcheckload $i)
		[ -z "$SERVER_LB_LOAD" ] && continue
		
		if [ $SERVER_LB_LOAD -gt $SERVER_LB_MAX ]
		then
			SERVER_LB_MAX=$SERVER_LB_LOAD
			SERVER_LB_HOST=$i
		fi
	done
	echo $SERVER_LB_HOST
}

server_loadbalance()
{
	SERVER_HOST="127.0.0.1"
	if [ -n "$LOAD_BALANCE_SERVERS" ]
	then
		SERVER_HOST=""
		if [ -n "$PREFERRED_HOST" -a "$ENABLE_LOAD_BALANCE_PREFERENCE" = "1" ]
		then
			stringinstring " $PREFERRED_HOST " " $LOAD_BALANCE_SERVERS " && SERVER_HOST="$PREFERRED_HOST"
		fi
		
		# Fallback if still empty
		if [ -z "$SERVER_HOST" ]
		then
			case "$LOAD_BALANCE_ALGORITHM" in
				random)
					SERVER_HOST=$(server_loadbalance_random)
				;;
				round-robin)
					SERVER_HOST=$(server_loadbalance_round_robin)
				;;
				load)
					SERVER_HOST=$(server_loadbalance_load)
				;;
			esac
		fi
		
		[ -z "$SERVER_HOST" ] && SERVER_HOST="127.0.0.1"
		[ -n "$SERVER_HOST" ] && log 5 "Info: Load-Balancing (if possible) to $SERVER_HOST ..."
	fi
	echo "$SERVER_HOST"
}	

server_startrestore_session()
{
	ACTION="$1"
	
	server_get_params $CMD
	PARAMS=$SERVER_PARAMS
	PARAMS="$PARAMS&clientproto=$PROTO"
	CMDLINE=$PARAMS
	echo_x

	# special shadowed type
	[ "$ACTION" != "start" -a "$(getparam type)" = "vnc" ] && ACTION="shadow"

	if [ "$ACTION" = "shadow" ]
	then
		ACTION="start"
		uniqueid=$(getparam restore)
		[ -z "$uniqueid" ] && uniqueid=$(getparam id) # 1.4.0-5 compatibility
		CMDLINE=$(session_get "$uniqueid" 2>/dev/null)

		shadowdisplay=$(getparam display)
		shadowhost=$(getparam host)
		shadowcookie=$(getparam shadowcookie)
		shadowuser=$(getparam userName)

		[ "$shadowcookie" = "none" ] && shadowcookie=""

		if [ -z "$shadowdisplay"  ]
		then
			# check for DESKTOP_SHARING_IDS
			shadowdisplay=$(echo $DESKTOP_SHARING_IDS | tr ' ' '\n' | egrep "^$uniqueid=" | cut -d'=' -f2)
			shadowhost="127.0.0.1"
		fi

		if [ -z "$shadowdisplay" ]
		then
			echo_x "NX> 596 Could not find shadowed session $uniqueid. Session failed."
			echo_x "NX> 596 Sharing: $DESKTOP_SHARING_IDS"
			return 1
		fi

		[ "$shadowhost" = "127.0.0.1" ] && shadowhost=""

		# not the same user? So we have a shadow cookie, we add to xauth
		if [ -n "$shadowcookie" -a "$ENABLE_SESSION_SHADOWING_AUTHORIZATION" = "1" ]
		then
			# Ask for permission first:
			echo_x "NX> 726 Asking user for authorization to attach to session"
			export XAUTHORITY=".Xauthority-$RANDOM-$$"
	               	$COMMAND_XAUTH add "$shadowhost:$shadowdisplay" MIT-MAGIC-COOKIE-1 "$shadowcookie" >/dev/null 2>&1
			PERMISSION=$(
				$PATH_BIN/nxdialog -display $shadowhost:$shadowdisplay -dialog yesno -caption "Authorization Request" -message "Do you want to allow $USER to shadow your session?" 2>/dev/null &
				SHADOW_DIALOG_PID=$!
				
				I=0
				while kill -0 $SHADOW_DIALOG_PID 2>/dev/null
				do
					let I=I+1
					[ $I -gt "$AGENT_STARTUP_TIMEOUT" ] && kill $SHADOW_DIALOG_PID 2>/dev/null
					sleep 1
				done
				
				echo "no"
				)
				
			$COMMAND_XAUTH remove "$shadowhost:$shadowdisplay"
			rm -f "$XAUTHORITY"

			if [ "$PERMISSION" = "no" ]
			then
				# User answered NO
				echo_x "NX> 596 Error: Authorization refused by user: $shadowuser."
				return 1
			fi
		fi

		PARAMS="$PARAMS&shadowdisplay=$shadowdisplay&shadowhost=$shadowhost&shadowcookie=$shadowcookie&shadowuser=$shadowuser"
		CMDLINE=$PARAMS
	fi
	
	if [ "$ACTION" = "start" ]
	then

		# Hack for external RDP/RFB agents

		if [ "$ENABLE_EXTERNAL_NXDESKTOP" = "1" -a "$(getparam type)" = "windows" ]
		then
			type="windows-helper"
			application="$PATH_BIN/nxdesktop_helper"
			PARAMS="$PARAMS&type=$type&application=$application&freenx_export_agents=1"
			CMDLINE=$PARAMS
		fi
		 
		if [ "$ENABLE_EXTERNAL_NXVIEWER" = "1" -a "$(getparam type)" = "vnc" ]
		then
			type="vnc-helper"
			application="$PATH_BIN/nxviewer_helper"
			PARAMS="$PARAMS&type=$type&application=$application&freenx_export_agents=1"
			CMDLINE=$PARAMS
		fi
	fi
	
	# If we can't get the userip and SSHD_CHECK_IP is set to 1
	# we bail out.
	if [ -z "$SSH_CLIENT" -a -z "$SSH2_CLIENT" ]
	then 
		if [ "$SSHD_CHECK_IP" = "1" ]
		then
			echo_x "NX> 596 Session startup failed. (Missing SSH_CLIENT environment variable)"
			return 1
		else
			log 2 "Warning: Failed to determine the client IP."
			log 2 "Warning: The SSH_CLIENT or SSH2_CLIENT variable was not provided by SSHD."
			log 2 "Warning: Please set SSHD_CHECK_IP=1 if you want to refuse the connection."
		fi
	fi
	
	export ENCRYPTION=$(getparam encryption)
	
	if [ "$ENABLE_FORCE_ENCRYPTION" = "1" -a "$ENCRYPTION" != "1" ]
	then
			echo_x "NX> 596 Unencrypted sessions are not allowed."
			return 1
	fi

	if [ -x "$PATH_BIN/nxacl" ]
	then
			log 3 "Info: Using $PATH_BIN/nxacl to change session parameters or deny session."
			NEW_PARAMS=$($PATH_BIN/nxacl "$CMDLINE")
			if [ $? -ne 0 ]
			then
				echo_x "NX> 596 The session failed due to a nxacl policy setting: $NEW_PARAMS"
				return 1
			fi

			# check if the acl input did make sense
			if [ -n "$NEW_PARAMS" ]
			then
				PARAMS=$NEW_PARAMS
				CMDLINE=$PARAMS
			fi
	fi

	# check if there is a suspended session, which we could resume
	if [ "$ENABLE_AUTORECONNECT" = "1" -a "$ACTION" = "start" ]
	then
		restore=$(session_get_user_suspended "$USER" "Suspended")
		if [ -n "$restore" ]
		then
			PARAMS="$PARAMS&restore=$restore"
			CMDLINE=$PARAMS
			ACTION="resume"
		fi
	fi

	# as only $SSH_CLIENT or $SSH2_CLIENT will be set, this should work
	USERIP=$(echo $SSH_CLIENT $SSH2_CLIENT | cut -d" " -f1 | sed 's/::ffff://g')
	[ -z "$USERIP" ] && USERIP="*"
	if [ "$ACTION" = "start" -o "$ACTION" = "shadow" ]
	then
		server_check_session_count || return 1
		
		# Possibly do loadbalancing
		
		SERVER_HOST=$(server_loadbalance)
		
		# start nxnode
		SESS_DISPLAY=$DISPLAY_BASE
		let SESS_DISPLAY_LIMIT=$DISPLAY_BASE+$DISPLAY_LIMIT
	
		# stupid but working algo ...
			
		while true
		do
			while [ -e /tmp/.X$SESS_DISPLAY-lock -o -e "/tmp/.nX$SESS_DISPLAY-lock"  -o -e "/tmp/.X11-unix/X$SESS_DISPLAY" ]
			do
				let SESS_DISPLAY=$SESS_DISPLAY+1
			done

			# Check if there is already an agent running on that display on that host
			let AGENT_DISPLAY=$SESS_DISPLAY+6000
			if $COMMAND_NETCAT -z "$SERVER_HOST" $AGENT_DISPLAY 2>/dev/null
			then
				log 2 "Warning: Stray nxagent without .nX$SESS_DISPLAY-lock found on host:port $SERVER_HOST:$AGENT_DISPLAY."
				let SESS_DISPLAY=$SESS_DISPLAY+1
				continue
			fi

			# Now check for the other enabled services
			
			let SAMBA_DISPLAY=$SESS_DISPLAY+3000
			if [ "$(getparam 'samba')" = 1 ] && $COMMAND_NETCAT -z "$SERVER_HOST" $SAMBA_DISPLAY
			then
				log 2 "Warning: Skipping $SERVER_HOST:$AGENT_DISPLAY as samba port is not free."
				let SESS_DISPLAY=$SESS_DISPLAY+1
				continue
			fi
			
			let MEDIA_DISPLAY=$SESS_DISPLAY+7000
			if [ "$(getparam 'media')" = 1 ] && $COMMAND_NETCAT -z "$SERVER_HOST" $MEDIA_DISPLAY
			then
				log 2 "Warning: Skipping $SERVER_HOST:$AGENT_DISPLAY as media port is not free."
				let SESS_DISPLAY=$SESS_DISPLAY+1
				continue
			fi

			
			let CUPS_DISPLAY=$SESS_DISPLAY+9000
			if [ "$(getparam 'cups')" = 1 ] && $COMMAND_NETCAT -z "$SERVER_HOST" $CUPS_DISPLAY
			then
				log 2 "Warning: Skipping $SERVER_HOST:$AGENT_DISPLAY as cups port is not free."
				let SESS_DISPLAY=$SESS_DISPLAY+1
				continue
			fi

			SESS_LOCKFILE=$(mktemp "/tmp/.nX$SESS_DISPLAY-lock.XXXXXXXXX")
			
			# ln is an atomic operation
			ln "$SESS_LOCKFILE" "/tmp/.nX$SESS_DISPLAY-lock" 2>/dev/null && break
		done

		rm -f "$SESS_LOCKFILE"
		
		if [ "$SESS_DISPLAY" -gt "$SESS_DISPLAY_LIMIT" ]
		then
			echo_x "NX> 596 Error: Display limit exceeded. Please remove some files from /tmp/.X*-lock."
			rm -f "/tmp/.nX$SESS_DISPLAY-lock"
			return
		fi
	
		uniqueid=$(echo $[$RANDOM*$RANDOM] | $COMMAND_MD5SUM | cut -d" " -f1 | tr "[a-z]" "[A-Z]")

		FULL_PARAMS="$PARAMS&user=$USER&userip=$USERIP&uniqueid=$uniqueid&display=$SESS_DISPLAY&host=$SERVER_HOST"
		log_secure "6" "$FULL_PARAMS"

		# now update the session listing
		sessionRootlessMode=0
		[ "$(getparam rootless)" = "1" ] && sessionRootlessMode=1
		CMDLINE="a=b&$FULL_PARAMS"
		session_add $uniqueid "sessionName=$(getparam session)&display=$(getparam display)&status=Running&startTime=$(date +%s)&foreignAddress=$(getparam userip)&sessionRootlessMode=$sessionRootlessMode&type=$(getparam type)&sessionId=$uniqueid&creationTime=$(date +%s)&userName=$USER&serverPid=$SERVER_PID&screeninfo=$(getparam screeninfo)&geometry=$(getparam geometry)&host=$SERVER_HOST&shadowcookie=none"
	else
		uniqueid=$(getparam restore)
		[ -z "$uniqueid" ] && uniqueid=$(getparam id) # 1.4.0-5 compatibility
		session_change "$uniqueid" "foreignAddress" "$USERIP"

		CMDLINE=$(session_get "$uniqueid")
		FULL_PARAMS="$PARAMS&user=$USER&userip=$(getparam foreignAddress)&uniqueid=$uniqueid&display=$(getparam display)&status=$(getparam status)"
		SESS_DISPLAY=$(getparam display)
		SERVER_HOST=$(getparam host)
		[ -z "$SERVER_HOST" ] && SERVER_HOST="127.0.0.1"
		
		if [ "$ENABLE_ADVANCED_SESSION_CONTROL" = "1" ]
		then
			CMDLINE="$FULL_PARAMS"
			case "$(getparam session)" in
				"add "*)
					server_nxnode_start --applicationsession "$USER" "$FULL_PARAMS"
					echo_x "Quit"
					echo_x "NX> 999 Quit"
					exit 1
				;;
			esac
		fi
	fi

	# now start the node
	sleep $AGENT_STARTUP_TIMEOUT &
	SERVER_WAIT_PID=$!
	( server_nxnode_start_wait --"$ACTION"session $USER "$FULL_PARAMS" ) &
	SERVER_PID=$!
	disown $SERVER_PID
	
	wait $SERVER_WAIT_PID
	
	if [ $? -eq 0 ]
	then
		# Something went wrong ...
		[ "$ACTION" = "start" ] && session_fail $uniqueid
		echo_x "NX> 1004 Error: Session did not start."
		echo_x "NX> 596 Session $ACTION failed."
		echo_x "NX> 999 Bye"
		# FIXME: Send node signal to terminate
		exit 1
	fi
	
	# We have now an active reader
	nxnode_login_register_reader
}

# Session stage
while true
do
	echo_x -n "NX> 105 "
	unset CMD
	read CMD 2>/dev/null
	# FIXME?
	[ "$CMD" = "" ] && CMD="quit"
	
	# Logging 
	case "$CMD" in
		startsession*|restoresession*|addmount*|addprinter*)
			echo_secure "$CMD"
			log_secure "4" "$CMD"
		;;
		*)
			echo "$CMD"
			log "4" "$CMD"
		;;
	esac
	
	case "$CMD" in 
		quit|QUIT)
			echo_x "Quit"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		exit|EXIT)
			echo_x "Exit"
			echo_x "NX> 999 Bye"
			exit 0
		;;
		bye|BYE)
			echo_x "Bye" 1>&2
			echo_x "NX> 999 Bye" 1>&2
			if [ "$ENCRYPTION" = "1" ] 
			then 
				let PROXY_DISPLAY=$SESS_DISPLAY+4000
				$COMMAND_NETCAT $SERVER_HOST $PROXY_DISPLAY
				RC=$?
				# kill our parent sshd process
				kill $PPID
				exit $RC
			else
				echo_x "NX> 1001 Bye."
			fi
		;;
		startsession*)
			server_startrestore_session "start"
		;;
		list*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			CMDLINE=$PARAMS

		
			status=$(getparam status)

			if [ "$status" = "Suspended" -a -n "$(getparam screeninfo)" ]
			then
				session_list_user_suspended "$USER" "Suspended" "$(getparam screeninfo)" "$(getparam type)"
			elif [ "$status" = "Suspended,Running" -o "$status" = "Suspended" ] # since 1.4.0-5
			then
				# disabled due to problems with 1.4.0-5 client
				#session_list_user_suspended "$USER" 'Suspended$|^status=Running$' "$(getparam geometry)" "$(getparam type)" | log_tee
				session_list_user_suspended "$USER" 'Suspended' "$(getparam geometry)" "$(getparam type)"
			elif [ "$status" = "suspended,running" -o "$status" = "suspended" ] # since 1.5.0
			then
				status=$(echo $status | sed 's/,/$|^status=/g; s/suspended/Suspended/g; s/running/Running/g')
				[ "$ENABLE_SHOW_RUNNING_SESSIONS" = "0" ] && status="Suspended"
				session_list_user_suspended "$USER" "$status" "$(getparam geometry)" "$(getparam type)"
			elif [ "$(getparam type)" = "shadow" ]
			then
				session_list_user_suspended ".*" "Running" "" "shadow"
			else
				session_list_user "$USER" | log_tee
			fi
		;;
		suspend*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			CMDLINE=$PARAMS
			if session_find_id_user "$(getparam sessionid)" "$USER"
			then
				server_nxnode_start --suspend "$USER" "$PARAMS"
			fi
		;;
		terminate*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			CMDLINE=$PARAMS
			if session_find_id_user "$(getparam sessionid)" "$USER"
			then
				server_nxnode_start --terminate "$USER" "$PARAMS"
			fi
		;;
		restoresession*)
			server_startrestore_session "resume"
		;;
		attachsession*)
			server_startrestore_session "shadow"
		;;

		passwd)
			echo_x "NX> 113 Changing password of user '$USER'"
			echo_x -n "NX> 102 Current password:"
			read -r -s PASS
			ENC_PASS=$(passdb_get_crypt_pass "$PASS")
			REAL_PASS=$(passdb_get_pass "$USER")
			echo_x
			if [ "$ENC_PASS" = "$REAL_PASS" ]
			then
				echo_x -n "NX> 102 Password:"
				read -r -s NEW_PASS1
				
				if [ ${#NEW_PASS1} -lt 5 ]
				then
					echo_x "NX> 500 ERROR: incorrect password format, password must be long at least five characters"
					continue
				fi

				echo_x
				echo_x -n "NX> 102 Confirm password:"
				read -r -s NEW_PASS1
				echo_x
				if [ "$NEW_PASS1" = "$NEW_PASS2" ]
				then
					ENC_PASS=$(passdb_get_crypt_pass "$NEW_PASS1")
					passdb_chpass "$USER" "$ENC_PASS"
					echo_x "NX> 114 Password of user '$USER' changed"
				else
					echo_x "NX> 537 ERROR: passwords do not match"
				fi
			else
				echo_x "NX> 500 ERROR: current password doesn't match"
			fi
		;;
		addmount*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			# TODO: This redirection is crap here.
			server_nxnode_start --smbmount "$USER" "$PARAMS" >/dev/null 2>&1 | log_error >/dev/null
			echo_x "NX> 719 SMB filesystem: running"
		;;
		addprinter*)
			server_get_params $CMD
			PARAMS=$SERVER_PARAMS
			# TODO: This redirecion is crap here.
			server_nxnode_start --addprinter "$USER" "$PARAMS" >/dev/null 2>&1 | log_error >/dev/null
			echo_x "NX> 719 CUPS printer: running"
		;;
		*)
			# disabled for 1.4.0-5 snapshot client
			#echo_x "NX> 503 Error: undefined command: '$CMD'"
		;;
	esac
done

fi

#
# End of Main nxserver <--> nxclient communication module
#

################### PACKAGE cmd.bm ############################

#
# library functions for nxserver-commandline cmds
#

# Policy: All functions and variables need to start with CMD_ / cmd_

# Needed global vars: $NX_VERSION, $NX_LICENSE, $NX_ETC_DIR, $PATH_BIN, $NX_HOME_DIR, $SSH_AUTHORIZED_KEYS

# Needed package: passdb

cmd_usage()
{
	echo "NXSERVER - Version $NX_VERSION $NX_LICENSE" 1>&2
	echo "Usage: nxserver <option>" 1>&2

	if [ "$1" = "root" ]
	then
		echo "--adduser <user>: Add a new user" 1>&2
		echo "--passwd <user>: Change password of <user>" 1>&2
		echo "--deluser <user>: Remove a user from nx" 1>&2
		echo "--listuser: List enabled users" 1>&2
		echo "" 1>&2
		echo "--start: Start the nx server" 1>&2
		echo "--stop: Stop the nx server" 1>&2
		echo "--status: Show status of nx server" 1>&2
		echo "--restart: Restart the nx server. (start,stop)" 1>&2
		echo "" 1>&2
		echo "--list [ user | sessionid ]: List running sessions of user or sessionid " 1>&2
		echo "--history [ user | sessionid | clear ]: Show history [ of user | sessionid ] or clear the history" 1>&2
		echo "--terminate <user | :display | sessionid>: Terminate the session pointed to by" 1>&2
		echo "       sessionid or display, or all sessions of the specified user." 1>&2
		echo "       Use * for all sessions." 1>&2
		echo "--force-terminate: Like terminate, but removes also session info." 1>&2
		echo "--suspend <user | :display | sessionid>: Suspend the session pointed to by" 1>&2
		echo "       sessionid or display, or all sessions of the specified user." 1>&2
		echo "       Use * for all sessions." 1>&2
		echo "--cleanup: Terminates all running sessions. Useful after power-outage."
		echo "" 1>&2
		echo "--broadcast <message>: Send a message to all users" 1>&2
		echo "--send <user | :display | sessionid> <message>: Send a message to the specified user or sessionid" 1>&2
	else
		echo "--passwd: Change password" 1>&2
	fi
	exit 1
}


cmd_abort()
{
	echo -e "NX> 500" "$@" 1>&2
	echo "NX> 999 Bye" 1>&2
	exit 1
}

cmd_abort_success()
{
	echo "NX> 500" "$@" 1>&2
	echo "NX> 999 Bye" 1>&2
	exit 0
}

cmd_user_passwd()
{
	echo "NX> 100 NXSERVER - Version $NX_VERSION $NX_LICENSE"
	echo "Sorry: Password changing for user is _not_ implemented, yet."
	echo "Please login to NX-Server to change password"
	echo "or ask your local system administrator."
	#echo "NX> 113 Changing password of user '$USER'"
	#echo "Old password:"
	#read -s OLDPASS
	#echo "New password:"
	#read -s NEWPASS1
	#echo "Repeat:"
	#read -s NEWPASS2

}

cmd_passwd()
{
	CMD_CHUSER=$2
	egrep -q "^$CMD_CHUSER:" $NX_ETC_DIR/passwords || cmd_abort "Error: User $CMD_CHUSER not found in database."
	echo -n "New password: "
	read -r -s CMD_NEWPASS
	echo
	CMD_ENC_PASS=$(passdb_get_crypt_pass "$CMD_NEWPASS")
	passdb_chpass "$CMD_CHUSER" "$CMD_ENC_PASS"
	echo "Password changed."
}

cmd_checkpassdb()
{
	if [ "$ENABLE_PASSDB_AUTHENTICATION" != "1" ]
	then
		cmd_abort "Error: The passdb function is not activated in node.conf.\n\nMost probably your FreeNX setup will work out of the box without this\nfunctionality and you've been misleaded by an old tutorial or old\ndocumentation to do this step.\n\nIf however you really need this functionality, just set\nENABLE_PASSDB_AUTHENTICATION=\"1\" in node.conf.\n"
	fi

	return 0
}

cmd_adduser()
{
	CMD_CHUSER=$2
	
	[ ${#CMD_CHUSER} -ge 32 ] && cmd_abort "Error: User $CMD_CHUSER must be shorter than 32 characters."
	egrep -q "^$CMD_CHUSER:" $NX_ETC_DIR/passwords && cmd_abort "Error: User $CMD_CHUSER already in database."
	getent passwd "$CMD_CHUSER" >/dev/null || cmd_abort "Error: User $CMD_CHUSER not existing on local system. Can't add."
	passdb_add_user "$CMD_CHUSER"
}

cmd_deluser()
{
	CMD_CHUSER=$2
	egrep -q "^$CMD_CHUSER:" $NX_ETC_DIR/passwords || cmd_abort "Error: User $CMD_CHUSER not found in database."
	passdb_remove_user "$CMD_CHUSER"
}

cmd_listuser()
{
	echo "NX> 146 NX users list"
	echo
	echo "Username"
	echo "---------------"
	echo
	passdb_list_user
	echo
}

cmd_start()
{
	
	[ -f $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS ] && cmd_abort_success "ERROR: Service already running"
	mv $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS.disabled $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS
	echo "NX> 122 Service started"
}

cmd_stop()
{
	[ -f $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS ] || cmd_abort_success "Service was already stopped"
	mv $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS.disabled
	echo "NX> 123 Service stopped"
}

cmd_status()
{
	[ -f $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS ] && echo "NX> 110 NX Server is running"
	[ -f $NX_HOME_DIR/.ssh/$SSH_AUTHORIZED_KEYS ] || echo "NX> 110 NX Server is stopped"
}

cmd_restart()
{
	cmd_stop
	cmd_start
}

cmd_parse_2_params()
{
	if [ ${#1} -eq 32 ]
	then
		CMD_APARAMS="sessionid=sessionId{$1}"
	else
	if [ "$1" != "" ]
	then
		#egrep -q "^$1:" $NX_ETC_DIR/passwords || cmd_abort "Error: User $1 not found in database."
		CMD_APARAMS="user=$1"
	fi
	fi
	echo $CMD_APARAMS

}

cmd_parse_3_params()
{
	if [ ${#1} -eq 32 ]
	then
		CMD_APARAMS=$(session_find_id $1)
		[ -n "$CMD_APARAMS" ] || cmd_abort "Error: Session $1 could not be found."
	elif [ "${1:0:1}" = ":" ]
	then
		CMD_APARAMS=$(session_find_display "${1:1}")
		[ -n "$CMD_APARAMS" ] || cmd_abort "Error: No running sessions found for display $1."
	elif [ "$1" = "*" ]
	then
		CMD_APARAMS=$(session_find_all)
		[ -n "$CMD_APARAMS" ] || cmd_abort "Error: No running sessions found."
	elif [ "$1" != "" ]
	then
		#egrep -q "^$1:" $NX_ETC_DIR/passwords || cmd_abort "Error: User $1 not found in database."
		CMD_APARAMS=$(session_find_user "$1")
		[ -n "$CMD_APARAMS" ] || cmd_abort "Error: No running sessions found for user $1."
	else
		cmd_abort "Error: Not enough parameters."
	fi
	echo $CMD_APARAMS
}

cmd_list_suspended()
{
	CMD_PARAMS=$(cmd_parse_2_params "$2")
	[ -n "$2" -a -z "$CMD_PARAMS" ] && exit 1
	case $CMD_PARAMS in
		user=*)
			session_list_user_suspended $2 "Suspended"
		;;
	esac
}
cmd_list()
{
	CMD_PARAMS=$(cmd_parse_2_params "$2")
	[ -n "$2" -a -z "$CMD_PARAMS" ] && exit 1
	case $CMD_PARAMS in
		user=*)
			session_list_user $2
		;;
		sessionid=*)
			session_list $2
		;;
		*)
			session_list_all
		;;
	esac
}

cmd_history_clear()
{
	rm -f $NX_SESS_DIR/closed/*
	rm -f $NX_SESS_DIR/failed/*
}

cmd_history()
{
	if [ "$2" = "clear" ]
	then
		cmd_history_clear
	fi
	
	CMD_PARAMS=$(cmd_parse_2_params "$2")
	user=""
	sessid=""
	case $CMD_PARAMS in
		user=*)
			user="$2"
		;;
		sessionid=*)
			sessid="$2"
		;;
	esac

	session_history "$user" "$sessid"
}

cmd_terminate()
{
	CMD_PARAMS=$(cmd_parse_3_params "$2")
	[ -z "$CMD_PARAMS" ] && exit 1
	for i in $CMD_PARAMS;
	do
			CMDLINE=$(session_get_cmdline $i)
			cmd_sessionid=$(getparam sessionId)
			cmd_user=$(getparam userName)
			cmd_type=$(getparam type)
			cmd_status=$(getparam status)

			# is it a "good" session?
			case "$1" in 
			--suspend)
				if [ "$cmd_status" = "Running" ] && stringinstring "unix-" "$cmd_type"
				then
					echo "sessionid=$cmd_sessionid" | su - "$cmd_user" -c "$PATH_BIN/nxnode --suspend"
				fi
			;;
			--terminate)
				echo "sessionid=$cmd_sessionid" | su - "$cmd_user" -c "$PATH_BIN/nxnode --terminate"
			;;
			--force-terminate)
				echo "sessionid=$cmd_sessionid" | su - "$cmd_user" -c "$PATH_BIN/nxnode --terminate"
				session_close $cmd_sessionid
			;;
			esac
	done

}

cmd_send()
{
	if [ "$1" = "--broadcast" ]
	then
	  CMD_PARAMS=$(session_find_all)
	  [ -z "$CMD_PARAMS" ] && cmd_abort "Error: No running session could be found."
	else
	  CMD_PARAMS=$(cmd_parse_3_params "$2")
	  [ -z "$CMD_PARAMS" ] && exit 1
	  shift
	fi
	shift
	for i in $CMD_PARAMS;
	do
			CMDLINE=$(session_get_cmdline $i)
			cmd_display=$(getparam display)
			cmd_user=$(getparam userName)
			cmd_type=$(getparam type)
			cmd_status=$(getparam status)
			cmd_host=$(getparam host)

			# is it a "good" session?
			if [ "$cmd_status" = "Running" ] && stringinstring "unix-" "$cmd_type"
			then
				if [ "$cmd_host" = "127.0.0.1" -o "$cmd_host" = "localhost" ]
				then
					su - "$cmd_user" -c "$PATH_BIN/nxdialog --dialog ok --caption \"NX Administrator Message\" --message \"$@\" -display \":$cmd_display\" &"
				else
					ssh $cmd_host su - "$cmd_user" -c "'$PATH_BIN/nxdialog --dialog ok --caption \"NX Administrator Message\" --message \"$@\" -display \":$cmd_display\" &'"
				fi
			fi
	done
	#nxnode_start --send "$CMD_PARAMS"
}

#
# normal user available functions
#

if [ $UID -ne 0 -a "$ENABLE_USERMODE_AUTHENTICATION" != "1" ]
then
	[ "$1" = "--agent" ] && exec $PATH_BIN/nxnode "$@"
	[ "$1" != "--passwd" ] && cmd_usage
	cmd_user_passwd
	exit 0
fi

#
# root mode available functions
#

[ $# -lt 1 ] && cmd_usage "root"
[ "$1" = "--help" ] && cmd_usage "root"

if [ "$1" = "--version" ]
then
  echo "NXSERVER - Version $NX_VERSION $NX_LICENSE"
  exit 0
fi

CMD=$1

echo "NX> 100 NXSERVER - Version $NX_VERSION $NX_LICENSE"

case $CMD in
	# 
	# User functions ...
	# 
	--passwd)
		cmd_checkpassdb
		cmd_passwd "$@"
	;;
	--adduser|--useradd)
		cmd_checkpassdb
		cmd_adduser "$@"
	;;
	--deluser|--userdel)
		cmd_checkpassdb
		cmd_deluser "$@"
	;;
	--listuser|--userlist)
		cmd_checkpassdb
		cmd_listuser
	;;
	--start)
		cmd_start
	;;
	--stop)
		cmd_stop
	;;
	--status)
		cmd_status
	;;
	--restart)
		cmd_restart
	;;
	--list)
		cmd_list "$@"
	;;
	--list-suspended)
		cmd_list_suspended "$@"
	;;
	--history)
		cmd_history "$@"
	;;
	--terminate|--suspend|--force-terminate)
		cmd_terminate "$@"
	;;
	--cleanup)
		cmd_terminate "--force-terminate" "*"
	;;
	--send|--broadcast)
		cmd_send "$@"
	;;
	*)
		cmd_abort "Error: Function $CMD not implemented yet."
esac
echo "NX> 999 Bye"
