freenx-server/nxserver
2025-08-08 20:28:57 +02:00

1516 lines
46 KiB
Bash
Executable File

#!/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>.
# (c) 2008-23 by Dmitry Borisov <i@dimbor.ru>
#
# License: GNU GPL, version 2
shopt -s extglob
SHARED_CONFS="/usr/share/freenx-server"
. $SHARED_CONFS/nxfuncs
log() {
#args: <logstr>
[ "$NX_LOG_LEVEL" != "0" ] || return
[ -n "$1" ] && \
echo -n "[$(date "+%d.%m %T.%3N"): $$/$BASHPID] " >> "$NX_LOGFILE"
echo -e $@ >> "$NX_LOGFILE"
}
# Log in a way that is secure for passwords / cookies / ...
echo_secure() {
[ -n "$1" ] && \
echo -n "[$(date "+%d.%m %T.%3N")] " >> "$NX_LOGFILE"
local res=${@/password=+([^&])&/password=*&}
res=${res//password=\"+([^\"])\"/password=\"*\"}
echo "$res"
}
log_secure() {
#args: <loglevel> <logstr>
[ "$NX_LOG_LEVEL" != "0" ] || return
echo_secure $@ >> "$NX_LOGFILE"
}
log_tee() {
if [ "$NX_LOG_LEVEL" != "0" ]; then exec tee -a "$NX_LOGFILE"
else exec cat -
fi
}
log_error() {
if [ "$NX_LOG_LEVEL" != "0" ]; then exec tee -a "$NX_LOGFILE"
else exec cat -
fi
}
echo_x() { log "$@"; echo "$@"; }
############### PACKAGE session.bm #######################
#
# Library of session management functions
#
# Needed global vars: NX_SESS_DIR session_count session_count_user
# COMMAND_MD5SUM SESSION_USER_LIMIT SESSION_LIMIT SESSION_HISTORY
# user
# =================== sqlite3 stuff =====================
sqcols_sess="session_id, session, display, status, userip,\
rootless, type, user, screeninfo, geometry, host, shadowcookie,\
startTime, creationTime, endTime, agent_pid"
init_sess_db() {
local sess_cols="session_id TEXT PRIMARY KEY, session TEXT,\
display TEXT, status TEXT, userip TEXT, rootless INT, type TEXT,\
user TEXT, screeninfo TEXT, geometry TEXT, host TEXT,\
shadowcookie TEXT, startTime INT, creationTime INT, endTime INT,\
agent_pid TEXT"
local qstr="CREATE TABLE IF NOT EXISTS sessions.sess($sess_cols);"
qstr+="CREATE INDEX IF NOT EXISTS sessions.idx_status_user ON\
sess(status,user);"
q_dbe "$qstr"
}
#-------------------------------------------------------------------
session_list() {
#params: sessId ["format_times"]
#if you need to humane output put something in $2
local qc fls res r2="";
[ -n "$2" ] && { fls="session_id, session, display, status,\
userip, rootless, type, screeninfo, geometry, host, shadowcookie,\
datetime(startTime,'unixepoch','localtime') AS startTime,\
datetime(creationTime,'unixepoch','localtime') AS creationTime, user,\
datetime(endTime,'unixepoch','localtime') AS endTime";
} || { # original order of fields
fls=$sqcols_sess;
}
res=$(qa_dbe ".mode line sess\n" \
"SELECT $fls FROM sess WHERE session_id='$1' LIMIT 1;")
[ -n "$2" ] && { echo "$res"; return; }
local line k v;
while read line; do
k=$(trim "$(cutfn "$line" 0 '=')"); v=$(trim "$(cutfn "$line" 1 '=')") #"
r2+="$k=$v"$'\n'
done <<< "$res"
echo -n "$r2"
}
session_find_cmdstrs() {
#params: [sessid] [user] [display] [status="Running|Suspended"]
#ret: sessions command strings delimited by \n
local st wstr res;
st="Running|Suspended"; [ -n "$4" ] && st="$4"
wstr="WHERE $(str_eq_cond "status" "$st")"
[ -n "$1" ] && wstr+=" AND $(str_eq_cond "session_id" "$1")"
[ -n "$2" -a "$2" != "all" -a "$2" != ".*" ] && \
wstr+=" AND $(str_eq_cond "user" "$2")"
[ -n "$3" ] && wstr+=" AND $(str_eq_cond "display" "$3")"
res=$(qa_dbe ".mode line sess\n" \
"SELECT $sqcols_sess FROM sess $wstr ORDER BY startTime DESC;")
qtxt2cmdstrs "$res"
}
# Find all running session-filenames
session_find_all() { session_find_cmdstrs; }
# Find all running sessions of a id
session_find_id() { session_find_cmdstrs "$1"; }
# Finds out if a session belongs to a user
session_find_user() { session_find_cmdstrs "" "$1"; }
# Find all running sessions of a display
session_find_display() { session_find_cmdstrs "" "" "$1"; }
# Finds out if a session belongs to a user
session_find_id_user() {
#params: sessid [user]
local wstr c;
wstr="WHERE session_id='$1' AND status IN ('Running', 'Suspended')"
[ -n "$2" -a "$2" != "all" -a "$2" != ".*" ] && \
wstr+=" AND $(str_eq_cond "user" "$2")"
c=$(qa_dbe ".mode tabs sess\n" \
"SELECT count(session_id) FROM sess $wstr LIMIT 1;")
[ "$c" -gt "0" 2>/dev/null ] || return 1
return 0
}
# session_get <session_id>
session_get() { session_find_cmdstrs "$1"; }
# Get the first session, which can be resumed
session_get_user_suspended() {
#params: user status
#ret: session_id line[s] or empty
local wstr r a;
wstr="WHERE status='$2'AND user='$1'"
r=$(qa_dbe ".mode tabs sess\n" \
"SELECT count(session_id), session_id FROM sess $wstr ORDER BY startTime DESC LIMIT 1;")
a=($r); ((${#a[@]}==2)) && echo "${a[1]}"
}
session_count_user() {
#params: user [status]
# Count all sessions of a user
# and save it in session_count and session_count_user
local st wstr r u="" sc=""
session_count=0; session_count_user=0
st="Running|Suspended"; [ -n "$2" ] && st="$2"
wstr="WHERE $(str_eq_cond "status" "$st")"
if [ "$1" = ".*" -o "$1" = "all" ]; then
session_count=$(qa_dbe ".mode tabs sess\n" \
"SELECT count(*) FROM sess $wstr;") #"
session_count_user=$session_count
return 0
fi
r=$(qa_dbe ".mode tabs sess\n" \
"SELECT user,count(*) FROM sess $wstr GROUP BY user;") #"
while read u sc; do
[ -z "$sc" ] && continue
((session_count+=sc))
[ "$u" = "$1" ] && session_count_user=$sc
done <<< "$r"
return 0
}
session_user_acl_load() {
#arg: <user>
# load nx shadow acl for given user and store it to
# shadow_users shadow_oviews shadow_uauths variables
[ -n "$ugroups" ] || \
{ ugroups=$(groups $1); ugroups=$(trim "${ugroups#*:}"); }
local s ucond="'#$1','*${ugroups// /\',\'\*}','@all'"
local mode=".mode csv settings\n.separator '&'\n"
local qstr="SELECT value,val_type,val_depend FROM settings WHERE user IN ($ucond) \
AND key='@shadow@' ORDER BY user ASC, val_check DESC LIMIT 1;"
#log "$FUNCNAME: qstr='$qstr'" #debug
local ts=$(qa_dbe "$mode" "$qstr")
#log "$lp ts='$ts'" #debug
shadow_users=$(cutfn "$ts" 0 '&')
s=$(cutfn "$ts" 1 '&'); shadow_oviews=(${s//,/ })
s=$(cutfn "$ts" 2 '&'); shadow_uauths=(${s//,/ })
}
_session_list_user_suspended() {
# args: <user> <status,> [geometry] <type>
# use conf vars: COMMAND_MD5SUM
# use glob: user
local p pstrs pattern geom depth render udepth urender mode;
local options available session_id geo2 displays disp2;
local puser pshadowcookie pscreeninfo prootless pgeometry pstatus;
local ptype pdisplay psession_id psession
local shadow_all=""
[ "$4" = "shadow" ] && stringinstring ",all," ",$shadow_users," && shadow_all="1"
echo "NX> 127 Sessions list of user '$1' for reconnect:"
echo
if [ -z "$4" ]; then
echo "Display Type Session ID Options Depth Screensize Available Session Name"
echo "------- ---------------- -------------------------------- -------- ----- -------------- --------- ----------------------"
else
echo "Display Type Session ID Options Depth Screen Status Session Name"
echo "------- ---------------- -------------------------------- -------- ----- -------------- ----------- ------------------------------"
fi
pstrs=$(session_find_cmdstrs "" "$1" "" "$2")
while read p; do
[ -z "$p" ] && continue
puser=$(getparam "$p" user); pshadowcookie=$(getparam "$p" shadowcookie);
pscreeninfo=$(getparam "$p" screeninfo); prootless=$(getparam "$p" rootless)
pgeometry=$(getparam "$p" geometry); pstatus=$(getparam "$p" status)
ptype=$(getparam "$p" type); pdisplay=$(getparam "$p" display)
psession_id=$(getparam "$p" session_id); psession=$(getparam "$p" session)
if [ "$4" = "shadow" -a "$puser" != "$1" ]; then
[ "$ENABLE_SESSION_SHADOWING" = "1" ] || continue
[ -n "$NX_ACL_DIR" -a -z "$shadow_all" ] && {
#log "in nxacl present CHECK ,$puser, ; ,$shadow_users," #debug
stringinstring ",$puser," ",$shadow_users," || continue
}
fi
pattern='^([0-9]*x[0-9]*)x([0-9]*)\+?([^+]*)'
[[ $pscreeninfo =~ $pattern ]]
geom=${BASH_REMATCH[1]}; depth=${BASH_REMATCH[2]}; render=${BASH_REMATCH[3]}
[[ $3 =~ $pattern ]]
udepth=${BASH_REMATCH[2]}; urender=${BASH_REMATCH[3]}
mode="D"; [ "$prootless" = "1" ] && mode="-"
options="-"; stringinstring "fullscreen" "$3" && options="F"
[ "$pgeometry" = "fullscreen" ] || options="-"
[ "$urender" = "render" ] && options="${options}R${mode}--PSA"
[ "$urender" = "render" ] || options="${options}-${mode}--PSA"
[ "$udepth" = "$depth" -a "$urender" = "$render" ] && available=$pstatus
# 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
if [ "$4" = "shadow" ]; then
available=$pstatus
printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$pdisplay" "$ptype" "$psession_id" "$options" "$depth" "$geom" "$available" "$psession ($puser) (Shadowed)"
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" "$pdisplay" "$ptype" "$psession_id" "$options" "$depth" "$geom" "$available" "$psession"
fi
done <<< "$pstrs"
echo ""
echo ""
session_count_user "$1"
if [ "$session_count" -ge "$SESSION_LIMIT" -o \
"$session_count_user" -ge "$SESSION_USER_LIMIT" ]; then
echo "NX> 147 Server capacity: reached for user: $1"
else
echo "NX> 148 Server capacity: not reached for user: $1"
fi
}
session_list_user_suspended() {
local slcd=$(_session_list_user_suspended "$@")
echo "$slcd" | log_tee
}
session_list_user() {
#params: user [status="Running|Suspended"]
local st wstr
echo -n "NX> 127 Sessions list"
if [ -n "$1" -a "$1" != "all" ]; then echo " of user '$1'"
else echo ":"
fi
echo
echo "Server Display Username Remote IP Session ID"
echo "------ ------- --------------- --------------- --------------------------------"
st="Running|Suspended"; [ -n "$2" ] && st="$2"
wstr="WHERE $(str_eq_cond "status" "$st")"
[ -n "$1" -a "$1" != "all" ] && wstr+=" AND $(str_eq_cond "user" "$1")"
qa_dbe ".mode tabs sess\n SELECT host,display,user,userip,session_id\
FROM sess $wstr ORDER BY startTime DESC;" # ! check order !
}
session_history() {
#params: user session_id
local wstr=""
echo "NX> 127 Session list:"
echo
echo "User Remote IP Status Start Stop "
echo "------- --------------- --------------- -------------- ---------------"
[ -n "$1" -a "$1" != "all" ] && wstr="$(str_eq_cond "user" "$1")"
[ -n "$wstr" -a -n "$2" ] && wstr+=" AND "
[ -n "$2" ] && wstr+=" session_id='$2'"
[ -n "$wstr" ] && wstr="WHERE $wstr"
qa_dbe ".mode tabs sess\n SELECT user,userip,status,strftime('%d.%m-%H:%M:%S',startTime,'unixepoch','localtime'),\
strftime('%d.%m-%H:%M:%S',endTime,'unixepoch','localtime')\
FROM sess $wstr ORDER BY endTime;"
}
# remove all sessions older than $SESSION_HISTORY seconds in failed/closed.
session_cleanup() {
local checkTime st wstr;
[ "$SESSION_HISTORY" -gt "-1" ] || return
let checkTime=$(date +%s)-$SESSION_HISTORY
st="Finished|Failed";
wstr="WHERE $(str_eq_cond "status" "$st") AND startTime < $checkTime"
q_dbe "DELETE FROM sess $wstr;"
}
session_list_all() { session_list_user "all"; }
session_add() {
#params: <col1,col2...> <val1&val2...>
q_row_ins "sess" "$1" "$2"
}
session_change() {
# <session_id> <col1,col2...> <val1&val2...>
q_rows_upd "sess" "session_id='$1'" "$2" "$3"
}
# session_running <session_id> (? now dublicated session_find_id_user ?)
# return: true if running, false if not
session_running() { session_find_id_user "$1"; }
session_close() {
#param: <session_id>
if [ "$SESSION_HISTORY" = "0" ] ; then
q_dbe "DELETE FROM sess WHERE session_id='$1';"
else
session_change "$1" "status,endTime" "Finished&$(date +%s)"
fi
}
session_fail() {
#param: <session_id>
if [ "$SESSION_HISTORY" = "0" ] ; then
q_dbe "DELETE FROM sess WHERE session_id='$1';"
else
session_change "$1" "status,endTime" "Failed&$(date +%s)"
fi
}
#
# end of library
#
msg2agentdisplay() {
#args: <dlg_type> <message> <[host]:display> <cookie> <agent_pid>
# [window_caption] [timeo=$AGENT_STARTUP_TIMEOUT]
# return exit code of nxdialog
local capt="FREENX server"; [ -n "$6" ] && capt=$6
local timeo=$AGENT_STARTUP_TIMEOUT; [ -n "$7" ] && timeo=$7
local rc=0
$COMMAND_XAUTH add "$3" MIT-MAGIC-COOKIE-1 "$4" &>/dev/null
DISPLAY="$3" $PATH_BIN/nxdialog --display "$3" --parent $5 \
--dialog "$1" --caption "$capt" --message "$2" &>/dev/null &
local dlg_pid=$!
if [ $timeo -gt 0 ]; then # make a simple watchdog for nxdialog
( sleep $timeo"s"
kill -0 $dlg_pid &>/dev/null && kill $dlg_pid &>/dev/null; ) &
fi
if stringinstring "$1" "ok|error|panic"; then
sleep 0.5s # don't remove xauthority cookie right now
else # wait for anwers only
wait $dlg_pid
rc=$?
fi
$COMMAND_XAUTH remove "$3" &>/dev/null
return $rc
}
#
# Main nxserver <-> nxclient communication module
#
# config variables in use:
# COMMAND_MD5SUM COMMAND_NETCAT
# COMMAND_SESSREG COMMAND_SSH COMMAND_XAUTH
# ENABLE_AUTORECONNECT
# ENABLE_LOAD_BALANCE_PREFERENCE ENABLE_LOG_FAILED_LOGINS
# ENABLE_SESSION_SHADOWING_AUTHORIZATION
# ENABLE_USESSION LOAD_BALANCE_SERVERS
# NX_ETC_DIR NX_HOME_DIR NX_LICENSE NX_LOGFILE NX_LOG_LEVEL
# NX_SESS_DIR NX_VERSION
# SERVER_NAME SESSION_HISTORY SESSION_LIMIT SESSION_USER_LIMIT
# declare global variables:
declare -g cmd in_params proto login_success login_method ugroups=""
declare -g server_mode preferred_host session_count session_count_user
declare -g user2 ENCRYPTION proxy_display server_host
declare -g oifs rc NODE_HOSTNAME user fl_send="1"
declare -g shadow_users shadow_oviews shadow_uauths shadowviewonly shadowauth
server_mode="0"; [ "$USER" = "nx" ] && server_mode="1"
if [ "$server_mode" = "1" ]; then
# Start!
open_dbe $$
attach_db "$sq_settings_fn" ro || {
echo "NX> 500 Error: Unable to attach db file $sq_settings_fn";
exit_proc 1;
}
set_vars_from_db
sess_bd="$NX_SESS_DIR/sessions.sq3"
attach_db $sess_bd && init_sess_db
log "-- NX SERVER START: $@ - ORIG_COMMAND=$SSH_ORIGINAL_COMMAND"
# Get the hostname out of SSH_ORIGINAL_COMMAND
preferred_host=$(rematchfn "host=([^&]*)" "$SSH_ORIGINAL_COMMAND") #"
echo_x "HELLO NXSERVER - Version $NX_VERSION $NX_LICENSE"
# Login stage
while true; do
echo_x -n "NX> 105 "
read cmd
[ "$cmd" = "" ] && cmd="quit" # FIXME?
echo_x "$cmd"
case "$cmd" in
quit|QUIT)
echo_x "Quit"; echo_x "NX> 999 Bye"; exit_proc 0;
;;
exit|EXIT)
echo_x "Exit"; echo_x "NX> 999 Bye"; exit_proc 0;
;;
bye|BYE)
echo_x "Bye"; echo_x "NX> 999 Bye"; exit_proc 0;
;;
hello*|HELLO*)
proto=$(rematchfn 'Version ([[:digit:][:punct:]]+)' "$cmd") #'
echo_x "NX> 134 Accepted protocol: $proto"
;;
"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: ";
oifs="$IFS"; export IFS=$'\n';
read -r -s PASS; export IFS=$oifs; echo_x ""
log -n "Info: Auth method: "
# USER already logged in?
user=$user2
# SU based auth used ONLY
if [ "$login_success" = "0" ]; then
log -n "su "
LC_MESSAGES=C $COMMAND_SUDO -u $user -Svk -p "" \
&>/dev/null <<< "$PASS"
if [ $? -eq 0 ]; then login_success="1"; login_method="SU"; fi
fi
if [ "$login_success" = "1" ]; then
# Reread the config files (so that $USER.node.conf get sourced)
set_vars_from_db "" $user "only"
[ "$ENABLE_SESSION_SHADOWING" = "1" ] && session_user_acl_load $user
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_proc 1
fi
;;
esac
done
echo_x "NX> 103 Welcome to: $SERVER_NAME user: $user"
# remove old session infos from history
session_cleanup
#
# call it with: server_get_params $cmd # no ""!
#
server_get_params() {
shift;
local server_params=" $@"; server_params=${server_params// --/\&};
server_params=${server_params//\"/};
if [ "$server_params" = "" ]; then
echo_x -n "NX> 106 Parameters: "
read server_params2; server_params=${server_params2//%2B/+}
echo_x
fi
server_params=${server_params//%20/ };
server_params=${server_params//\&sessionid/\&session_id};
server_params=${server_params//\&id/\&session_id};
echo "$server_params"
}
server_nxnode_start() {
# use vars: session_id
# NODE_HOSTNAME NXNODE_TOSEND
local cmd="$1" user="$2"; shift; shift;
# Find NODE_HOSTNAME
#NODE_HOSTNAME=""
#CMDLINE="$@"; session_id=$(getparam session_id)
#CMDLINE=$(session_get "$session_id")
NODE_HOSTNAME=$host; [ -z "$NODE_HOSTNAME" ] && NODE_HOSTNAME="127.0.0.1"
export NODE_HOSTNAME
echo -e "$PASS\n$@" | \
$COMMAND_SUDO -u $user -HSik -p "" $PATH_BIN/nxnode "$cmd" 2>&1 | \
log_tee
}
server_add_usession() {
[ "$ENABLE_USESSION" = "1" ] || return
$COMMAND_SESSREG -l ":$display" -h "$userip" -a $user 2>&1 | \
log_error
}
server_remove_usession() {
[ "$ENABLE_USESSION" = "1" ] || return
$COMMAND_SESSREG -l ":$display" -h "$userip" -d $user 2>&1 | \
log_error
}
server_nxnode_echo() {
if [ "$server_channel" = "1" ]; then
echo -e "$@"
log "$FUNCNAME >&$server_channel: $@"
elif [ "$server_channel" = "2" ]; then
echo -e "$@" >&2
log "$FUNCNAME >&$server_channel: $@"
fi
}
server_nxnode_exit_func() {
log "$FUNCNAME: Info: Emergency-Shutting down due to kill signal ..."
session_fail $session_id
server_remove_usession
# remove lock file
[ -e "/tmp/.nX$display-lock" ] && rm -f /tmp/.nX$display-lock
exit_proc 1
}
server_nxserver_exit_func() {
log "$FUNCNAME: Info: Emergency-Shutting down!"
[ -n "$adm_pid" ] && kill -0 $adm_pid &>/dev/null \
&& kill $adm_pid &>/dev/null
exit_proc 1
}
server_nxnode_start_wait() {
# use uplevel: $server_wait_pid $user $session_id
local lp=" $FUNCNAME:";
local server_channel=1 kill_wait_pid=1 shadowcookie agent_pid
local fl_start="" to_send=""
open_dbe $BASHPID
[ "$1" = "--startsession" ] && fl_start="1";
if [ -n "$fl_start" ]; 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
fi
server_nxnode_start "$@" | while read cmd; do
case "$cmd" in
"NX> 700"*)
server_channel=0; to_send="$cmd"
;;
"NX> 706"*)
shadowcookie=$(cutfn "$cmd" 1 ':'); shadowcookie=${shadowcookie// /}
session_change "$session_id" "shadowcookie" "$shadowcookie"
to_send+="\n$cmd"
;;
"NX> 733"*)
agent_pid=$(cutfn "$cmd" 1 ':'); agent_pid=${agent_pid// /}
session_change "$session_id" "agent_pid" "$agent_pid"
;;
"NX> 70"*)
to_send+="\n$cmd"
;;
"NX> 710"*)
to_send+="\n$cmd"; server_channel=1;
server_nxnode_echo "$to_send";
server_channel=0;
to_send="";
kill -SIGUSR2 $$ 2>/dev/null
continue
;;
"NX> 1006"*|"NX> 1005"*|"NX> 1009"*)
case "$cmd" in
*running*)
[ "$kill_wait_pid" = "1" ] && kill -INT $server_wait_pid 2>/dev/null
kill_wait_pid=0
if [ "$server_channel" = "1" ]; then
server_channel=2
fi
log "$lp set status $session_id: Running"
session_change $session_id "status" "Running"
;;
*starting*)
log "$lp set status $session_id: Starting"
session_change $session_id "status" "Starting"
continue;
;;
*resuming*)
log "$lp set status $session_id: Resuming"
session_change $session_id "status" "Resuming"
continue;
;;
*closed*)
if [ -n "$fl_start" ]; then
log "$lp session_close $session_id"
session_close $session_id; break;
fi
;;
*suspended*)
if [ -n "$fl_start" ]; then
[ "$kill_wait_pid" = "1" ] && kill -INT $server_wait_pid 2>/dev/null
kill_wait_pid=0; log "$lp session suspend $session_id"
session_change $session_id "status,userip" "Suspended&-"
fi
;;
*suspending*)
if [ -n "$fl_start" ]; then
log "$lp set status $session_id: Suspending"
session_change $session_id "status" "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
fi
;;
*terminating*)
if [ -n "$fl_start" ]; then
log "$lp set status $session_id: Terminating"
session_change $session_id "status" "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
fi
;;
esac
;;
"NX> 1004"*)
[ "$kill_wait_pid" = "1" ] && {
kill -INT $server_wait_pid 2>/dev/null
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 $session_id
server_nxnode_echo "NX> 596 Session startup failed."
log "NX> 596 Session startup failed."
[ -z "$fl_start" ] && break; # on restore only?
;;
"NX> 1001"*)
if [ -z "$fl_start" ]; then
server_channel=0
log "$lp nxnode finished (1001) on display $display"
close_dbe $BASHPID; exit_proc 0
fi
;;
esac
case $cmd in "NX> "*) server_nxnode_echo $cmd;; esac
done
if [ "$1" = "--startsession" ]; then
trap - EXIT
trap - SIGPIPE
# Close it in case the session is still running
session_running $session_id && session_close $session_id
server_remove_usession
# remove lock file
[ -e "/tmp/.nX$display-lock" ] && rm -f /tmp/.nX$display-lock
fi
close_dbe $BASHPID
log "$lp $1 end on display $display"
}
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
local server_lb_hosts=( $LOAD_BALANCE_SERVERS )
local server_lb_nr_of_hosts=${#server_lb_hosts[@]}
let server_lb_nr=(RANDOM % server_lb_nr_of_host)
local server_lb_host=${server_lb_hosts[$server_lb_nr]}
echo $server_lb_host
}
# run in subshell!
server_loadbalance_round_robin() {
local server_lb_hosts=( $LOAD_BALANCE_SERVERS )
local 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
LC_MESSAGES=C sleep 0.01
((i++))
done
if [ $i -ge 200 ]; then
log "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() {
local server_lb_max=0 server_lb_host="" server_lb_load;
export PATH_BIN
for i in $LOAD_BALANCE_SERVERS; do
server_lb_load=$($COMMAND_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() {
local 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 "Info: Load-Balancing (if possible) to $server_host ..."
fi
echo "$server_host"
}
server_startrestore_session() {
local action="$1" params="$2" rc sess_lockfile
local agent_display samba_display cups_display media_display restore
local server_pid server_wait_pid new_params db_params nshu="-1" i s
params+="&clientproto=$proto&login_method=$login_method"
echo_x
if [ "$action" = "shadow" ]; then
shadowauth="0"; shadowviewonly="0"
action="start"; params=$(delparam "$params" "display");
params=$(delparam "$params" "session_id");
db_params=$(session_get "$in_session_id" 2>/dev/null)
set_vars_from_ampstr "$db_params" "db_"
encryption=$in_encryption
shadowdisplay=$in_display; shadowhost=$db_host;
shadowuser=$db_user; shadowcookie=$db_shadowcookie
[ "$shadowcookie" = "none" ] && shadowcookie=""
[ "$in_shadowviewonly" = "1" ] && shadowviewonly="1"
if [ -z "$shadowdisplay" ]; then
echo_x "NX> 596 Could not find shadowed session $session_id. Session failed."
exit_proc 1
fi
[ "$shadowhost" = "127.0.0.1" ] && shadowhost=""
if [ "$user" != "$shadowuser" -a \
"$ENABLE_SESSION_SHADOWING_AUTHORIZATION" = "1" ]; then
shadowauth="1"
[ -n "$shadow_users" ] && {
local shu=(${shadow_users//,/ })
for ((i=0; i<${#shu[@]}; i++)) {
[ "${shu[$i]}" = "$shadowuser" -o "${shu[$i]}" = "all" ] && {
nshu=$i; break;
}
}
}
((nshu>=0)) && {
[ "$shadowviewonly" = "0" ] && shadowviewonly=${shadow_oviews[$nshu]}
shadowauth=${shadow_uauths[$nshu]}
}
fi
if [ "$shadowauth" = "1" ]; then
# Ask for permission first:
echo_x "NX> 726 Asking user for authorization to attach to session"
msg2agentdisplay "yesno" \
"Do you want to allow $user to shadow your session?" \
$shadowhost:$shadowdisplay $shadowcookie $db_agent_pid \
"Authorization Request"
if [ "$?" != "1" ]; then
# User answered NO or time out
echo_x "NX> 596 Error: Authorization refused by user: $shadowuser."
exit_proc 1
fi
fi
params+="&shadowdisplay=$shadowdisplay&shadowhost=$shadowhost&\
shadowcookie=$shadowcookie&shadowuser=$shadowuser&\
shadowviewonly=$shadowviewonly&shadowauth=$shadowauth"
fi
[ "$action" = "start" ] && \
[ "$type" = "windows" -o "$type" = "vnc" ] && \
params=${params//&type=$type&/&type=$type'-helper'&}
# 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 "Warning: Failed to determine the client IP."
log "Warning: The SSH_CLIENT or SSH2_CLIENT variable was not provided by SSHD."
log "Warning: Please set SSHD_CHECK_IP=1 if you want to refuse the connection."
fi
fi
export ENCRYPTION=$encryption
if [ "$ENABLE_FORCE_ENCRYPTION" = "1" -a "$ENCRYPTION" != "1" ]; then
echo_x "NX> 596 Unencrypted sessions are not allowed."
exit_proc 1
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
session_id=$restore; action="resume"
fi
fi
# as only $SSH_CLIENT or $SSH2_CLIENT will be set, this should work
userip=$(rematchfn "($ip4_pattern)" "$SSH_CLIENT $SSH2_CLIENT") #"
[ -z "$userip" ] && userip="*"
if [ "$action" = "start" -o "$action" = "shadow" ]; then
server_check_session_count || exit_proc 1
# Possibly do loadbalancing
server_host=$(server_loadbalance)
# start nxnode
display=$DISPLAY_BASE
((sess_display_limit=DISPLAY_BASE+DISPLAY_LIMIT))
# stupid but working algo ...
while true; do
while [ -e /tmp/.X$display-lock -o \
-e "/tmp/.nX$display-lock" -o \
-e "/tmp/.X11-unix/X$display" ]; do
((display++))
done
# Check if there is already an agent running on that display on that host
((agent_display=display+6000))
if port_is_listening $agent_display "$server_host"; then
log "Warning: Stray nxagent without .nX$display-lock found on host:port $server_host:$agent_display."
((display++))
continue
fi
((proxy_display=display+4000))
if port_is_listening $proxy_display "$server_host"; then
log "Warning: nxagent proxy without .nX$display-lock found on host:port $server_host:$agent_display."
((display++))
continue
fi
# Now check for the other enabled services
((samba_display=display+3000))
if [ "$samba" = 1 ] && \
port_is_listening $samba_display "$server_host"; then
log "Warning: Skipping $server_host:$agent_display as samba port is not free."
((display++))
continue
fi
((media_display=display+7000))
if [ "$media" = 1 ] && \
port_is_listening $media_display "$server_host"; then
log "Warning: Skipping $server_host:$agent_display as media port is not free."
((display++))
continue
fi
((cups_display=display+9000))
if [ "$cups" = 1 ] && \
port_is_listening $cups_display "$server_host"; then
log "Warning: Skipping $server_host:$agent_display as cups port is not free."
((display++))
continue
fi
sess_lockfile=$(mktemp "/tmp/.nX$display-lock.XXXXXXXXX")
# ln is an atomic operation
ln "$sess_lockfile" "/tmp/.nX$display-lock" 2>/dev/null && break
done
rm -f "$sess_lockfile"
if [ "$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$display-lock"
exit_proc 1
fi
session_id=$(echo $[$RANDOM*$RANDOM] | $COMMAND_MD5SUM)
session_id=${session_id%% *}; session_id=${session_id^^}
params+="&user=$user&userip=$userip&session_id=$session_id&\
display=$display&host=$server_host"
log_secure "$params"
# now update the session listing
local time_now=$(date +%s)
session_add "session_id,user,session,display,status,userip,rootless,\
type,screeninfo,geometry,host,shadowcookie,startTime,creationTime"\
"$session_id&$user&$session&$display&Running&$userip&$rootless&\
$type&$screeninfo&$geometry&$server_host&none&$time_now&$time_now"
else
[ -z "$(getparam "$params" "session_id")" ] && params+="&session_id=$session_id"
session_change "$session_id" "userip" "$userip"
db_params=$(session_get "$session_id");
display=$(getparam "$db_params" display);
host=$(getparam "$db_params" host); server_host=$host
[ -z "$server_host" ] && server_host="127.0.0.1"
status=$(getparam "$db_params" status)
params+="&host=$server_host&user=$user&userip=$userip&display=$display&status=$status"
if [ "$ENABLE_ADVANCED_SESSION_CONTROL" = "1" ]; then
case "$session" in
"add "*)
server_nxnode_start --applicationsession "$user" "$params"
echo_x "Quit"
echo_x "NX> 999 Quit"
exit_proc 1
;;
esac
fi
fi
# now start the node
sleep $AGENT_STARTUP_TIMEOUT &
server_wait_pid=$!
( server_nxnode_start_wait --"$action"session $user "$params" ) &
server_pid=$!; disown $server_pid
wait $server_wait_pid 2>/dev/null
if [ $? -eq 0 ]; then
# Something went wrong ...
[ "$action" = "start" ] && session_fail $session_id
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_proc 1
fi
}
# Session stage
trap "fl_send=1" SIGUSR2
while true; do
[ "$fl_send" = "0" ] && { sleep 0.01s; continue; }
echo_x -n "NX> 105 "
unset cmd
read cmd 2>/dev/null
[ "$cmd" = "" ] && cmd="quit" # FIXME?
# Logging
case "$cmd" in
startsession*|restoresession*|addmount*|addprinter*)
echo_secure "$cmd"; log_secure "$cmd"
;;
*)
echo "$cmd"; log "> $cmd"
;;
esac
case "$cmd" in
quit|QUIT)
echo_x "Quit"
echo_x "NX> 999 Bye"
exit_proc 0
;;
exit|EXIT)
echo_x "Exit"
echo_x "NX> 999 Bye"
exit_proc 0
;;
bye|BYE)
echo_x "Bye" 1>&2
echo_x "NX> 999 Bye" 1>&2
if [ "$ENCRYPTION" = "1" ]; then
((proxy_display=display+4000))
log "Session stage: proxy display starting $server_host:$proxy_display"
$COMMAND_NETCAT $server_host $proxy_display 2>/dev/null; rc=$?
log "Session stage: proxy display finished rc=$rc"
kill $PPID 2>/dev/null # kill our parent sshd process
exit_proc $rc
else
echo_x "NX> 1001 Bye."
fi
;;
admin)
[ -n "$ugroups" ] || \
{ ugroups=$(groups "$user"); ugroups=$(trim "${ugroups#*:}"); }
if stringinstring " nxadmin " " $ugroups "; then
trap server_nxserver_exit_func EXIT
log "User '$user': admin mode starting"
LC_MESSAGES=C $COMMAND_SUDO -u $user -S -v -p "" <<< "$PASS"
$COMMAND_SUDO -u $user -H /bin/bash -c \
'/usr/bin/nxnode --admin'
log "User '$user': admin mode stopping with rc=$?"
echo_x "NX> 1001 Bye."
trap EXIT
exit_proc 0
else
log "User '$user': admin mode start failed"
echo_x "NX> 2004 admin mode start failed"; exit_proc 1;
echo_x "NX> 1001 Bye."
fi
;;
startsession*)
fl_send="0"; in_params=$(server_get_params $cmd)
set_vars_from_ampstr "$in_params" # set global vars without prefixes
server_startrestore_session "start" "$in_params"
;;
list*)
# used: user,status, type, screeninfo == geometry ???
in_params=$(server_get_params $cmd)
set_vars_from_ampstr "$in_params" "in_"
if [ "$in_status" = "Suspended" -a -n "$in_screeninfo" ]; then
session_list_user_suspended "$in_user" "Suspended" \
"$in_screeninfo" "$in_type"
elif [ "$in_status" = "Suspended,Running" -o "$in_status" = "Suspended" ]; 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 "$in_user" \
'Suspended' "$in_geometry" "$in_type"
elif [ "$in_status" = "suspended,running" -o "$in_status" = "suspended" ]; then
# since 1.5.0
[ "$ENABLE_SHOW_RUNNING_SESSIONS" = "0" ] && in_status="Suspended" || {
in_status=${in_status/,/|}
in_status=${in_status/suspended/Suspended}
in_status=${in_status/running/Running}
}
session_list_user_suspended "$in_user" "$in_status" \
"$in_geometry" "$in_type"
elif [ "$in_type" = "shadow" ]; then
session_list_user_suspended ".*" "Suspended|Running" "" "shadow"
else
session_list_user "$in_user" | log_tee
fi
;;
suspend*)
# used: user, session_id
in_params=$(server_get_params $cmd)
set_vars_from_ampstr "$in_params"
[ -z "$display" ] && \
display=$(q_vals_str_get "sess" \
"session_id='$session_id'" "display")
in_params+="&display=$display"
if session_find_id_user "$session_id" "$user"; then
server_nxnode_start --suspend "$user" "$in_params"
fi
;;
terminate*)
in_params=$(server_get_params $cmd)
set_vars_from_ampstr "$in_params"
[ -z "$display" ] && \
display=$(q_vals_str_get "sess" \
"session_id='$session_id'" "display")
in_params+="&display=$display"
if session_find_id_user "$session_id" "$user"; then
server_nxnode_start --terminate "$user" "$in_params"
fi
;;
restoresession*)
fl_send="0"; in_params=$(server_get_params $cmd)
set_vars_from_ampstr "$in_params"
[ -z "$display" ] &&
display=$(q_vals_str_get "sess" \
"session_id='$session_id'" "display")
in_params+="&display=$display"
server_startrestore_session "resume" "$in_params"
;;
attachsession*)
in_params=$(server_get_params $cmd)
set_vars_from_ampstr "$in_params" "in_"
server_startrestore_session "shadow" "$in_params"
;;
addmount*)
in_params=$(server_get_params $cmd)
set_vars_from_ampstr "$in_params" "in_"
in_params+="&display=$display"
( server_nxnode_start --smbmount "$user" "$in_params" >/dev/null 2>&1 ) &
;;
addprinter*)
in_params=$(server_get_params $cmd)
set_vars_from_ampstr "$in_params" "in_"
in_params+="&display=$display"
( server_nxnode_start --addprinter "$user" "$in_params" >/dev/null 2>&1 ) &
;;
*)
# disabled for 1.4.0-5 snapshot client
#echo_x "NX> 503 Error: undefined command: '$cmd'"
;;
esac
done
trap - SIGUSR2
fi # server_mode == "1"
#
# 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 "--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 | session_id ]: List running sessions of user or session_id " 1>&2
echo "--history [ user | session_id | clear ]: Show history [ of user | session_id ] or clear the history" 1>&2
echo "--terminate <user | :display | session_id>: Terminate the session pointed to by" 1>&2
echo " session_id 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 | session_id>: Suspend the session pointed to by" 1>&2
echo " session_id 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 | session_id> <message>: Send a message to the specified user or session_id" 1>&2
fi
exit_proc 1
}
cmd_abort() {
echo -e "NX> 500" "$@" 1>&2
echo "NX> 999 Bye" 1>&2
exit_proc 1
}
cmd_abort_success() {
echo "NX> 500" "$@" 1>&2
echo "NX> 999 Bye" 1>&2
exit_proc 0
}
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()
{
local cmd_params;
if [ ${#1} -eq 32 ]; then cmd_params="session_id=$1"
elif [ "$1" != "" ]; then cmd_params="user=$1"
fi
echo "$cmd_params"
}
cmd_parse_3_params()
{
local cmd_params;
if [ ${#1} -eq 32 ]; then
cmd_params=$(session_find_id $1)
[ -n "$cmd_params" ] || cmd_abort "Error: Session $1 could not be found."
elif [ "${1:0:1}" = ":" ]; then
cmd_params=$(session_find_display "${1:1}")
[ -n "$cmd_params" ] || cmd_abort "Error: No running sessions found for display $1."
elif [ "$1" = "*" ]; then
cmd_params=$(session_find_all)
[ -n "$cmd_params" ] || cmd_abort "Error: No running sessions found."
elif [ "$1" != "" ]; then
cmd_params=$(session_find_user "$1")
[ -n "$cmd_params" ] || cmd_abort "Error: No running sessions found for user $1."
else cmd_abort "Error: Not enough parameters."
fi
echo "$cmd_params"
}
cmd_list_suspended() {
local cmd_params=$(cmd_parse_2_params "$2")
[ -n "$2" -a -z "$cmd_params" ] && exit_proc 1
case $cmd_params in
user=*)
session_list_user_suspended $2 "Suspended"
;;
esac
}
cmd_list() {
local cmd_params=$(cmd_parse_2_params "$2")
[ -n "$2" -a -z "$cmd_params" ] && return
case $cmd_params in
user=*)
session_list_user $2
;;
session_id=*)
session_list $2
;;
*)
session_list_all
;;
esac
}
cmd_history_clear() {
q_dbe "DELETE FROM sess WHERE status IN ('Finished','Failed');"
}
cmd_history() {
local cmd_params user="" sessid=""
if [ "$2" = "clear" ]; then cmd_history_clear; return; fi
cmd_params=$(cmd_parse_2_params "$2")
case $cmd_params in
user=*)
user="$2"
;;
session_id=*)
sessid="$2"
;;
esac
session_history "$user" "$sessid"
}
cmd_execute() {
local cmd_host="$1" cmd_user="$2" cmd_cmd="$3"
if [ "$cmd_host" = "127.0.0.1" -o "$cmd_host" = "localhost" ]; then
/bin/su - "$cmd_user" -c "$cmd_cmd"
else
ssh "$cmd_host" su - "$cmd_user" -c "'$cmd_cmd'"
fi
}
cmd_terminate_or_send() {
local cmd="$1" cmd_params lineamp cmd_shadowcookie cmd_agent_pid;
local cmd_session_id cmd_display cmd_user cmd_type cmd_status cmd_host;
if [ "$cmd" = "--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_proc 1
shift
fi
shift
while read lineamp; do
[ -z "$lineamp" ] && continue
cmd_session_id=$(getparam "$lineamp" session_id)
cmd_display=$(getparam "$lineamp" display)
cmd_user=$(getparam "$lineamp" user)
cmd_type=$(getparam "$lineamp" type)
cmd_status=$(getparam "$lineamp" status)
cmd_host=$(getparam "$lineamp" host)
# is it a "good" session?
case "$cmd" in
--suspend)
if [ "$cmd_status" = "Running" ] && stringinstring "unix-" "$cmd_type"
then
echo "session_id=$cmd_session_id&display=$cmd_display" | \
cmd_execute "$cmd_host" "$cmd_user" "$PATH_BIN/nxnode --suspend"
fi
;;
--terminate)
echo "session_id=$cmd_session_id&display=$cmd_display" | \
cmd_execute "$cmd_host" "$cmd_user" "$PATH_BIN/nxnode --terminate"
;;
--force-terminate)
echo "session_id=$cmd_session_id&display=$cmd_display" | \
cmd_execute "$cmd_host" "$cmd_user" "$PATH_BIN/nxnode --terminate"
session_close $cmd_session_id
;;
--send|--broadcast)
# is it a "good" session?
if [ "$cmd_status" = "Running" ]; then
cmd_agent_pid=$(getparam "$lineamp" agent_pid)
cmd_shadowcookie=$(getparam "$lineamp" shadowcookie)
msg2agentdisplay "ok" "$(date "+%d.%m %H:%M"): $@" \
:$cmd_display $cmd_shadowcookie $cmd_agent_pid \
"NX Admin Message" 0
fi
esac
done <<< "$cmd_params"
}
sqcols_sess_adm="user, session, status, creationTime, startTime, endTime,\
type, session_id, display, userip, rootless, screeninfo, geometry, host"
cmd_list0() {
#args: term1[&term2...] [sort1[!][,sort2...]]
# term: <exp><cond><val_str>
# cond: = != > < >= <= ; val_str: val1[|val2...] or val_start,val_end
# if '!' present in sortN then sort order is DESC else ASC
local colstr="" wstr="" sortstr="" res
[ "$admin_mode" = "1" ] && colstr=$sqcols_sess_adm || colstr=$sqcols_sess
[ -n "$1" ] && wstr="WHERE $(q_where_str "$1")"
[ -n "$2" ] && sortstr="ORDER BY $(q_sort_str "$2")"
local mode=".mode csv sess\n.separator '&'\n"
local qstr="SELECT $colstr FROM sess $wstr $sortstr;" #; echo "$qstr" #debug
res=$(qa_dbe0 "$mode" "$qstr")
echo_x "NX> 2126 $colstr"
echo_x "NX> 2127 strings list start"
echo "${res//\"/}"
echo_x "NX> 2148 strings list end"
}
open_dbe $$
attach_db "$sq_settings_fn" ro || {
echo "NX> 500 Error: Unable to attach db file $sq_settings_fn";
exit_proc 1;
}
set_vars_from_db
sess_bd="$NX_SESS_DIR/sessions.sq3"
if [ "$NX_LOG_LEVEL" != "0" -a ! -f "$NX_LOGFILE" ]; then
touch "$NX_LOGFILE" >/dev/null 2>&1
fi
if [ "$UID" -eq "0" ]; then
chown nx "$NX_LOGFILE" >/dev/null 2>&1; chmod 660 "$NX_LOGFILE"
chown nx "$sess_bd" >/dev/null 2>&1; chmod 660 "$sess_bd"
fi
attach_db $sess_bd && init_sess_db
#
# normal user available functions
if [ $UID -ne 0 ]; then
if [ "$1" = "--agent" ]; then exec $PATH_BIN/nxnode "$@"
else cmd_usage
fi
exit_proc 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_proc 0
fi
declare -g admin_mode="" mloop="1" cmd0 q s l
[ "$1" = "--admin" ] && { # admin mode loop
admin_mode="1"; shift;
trap exit_proc SIGINT; trap exit_proc SIGKILL; trap exit_proc EXIT
echo_x "NX> 2103 admin mode started"
while [ "$mloop" = "1" ]; do
echo_x -n "NX> 2105 "
unset cmd0; read cmd0 2>/dev/null
[ "$cmd0" = "" ] && cmd0="quit" # FIXME?
# do fake eval for params
q=0; l=$cmd0; cmd=()
while [ -n "$l" ]; do
s=${l%%[\'\"]*}; l=${l#*[\'\"]}; [ "$s" = "$l" ] && l=""
if ((q==0)); then
[ -n "$s" ] || continue
cmd+=($s); q=1
else cmd+=("$s"); q=0
fi
done
#log "${#cmd[@]}" #debug
#for ((i=0; i<${#cmd[@]}; i++)); do log "${cmd[$i]}"; done #debug
case ${cmd[0]} in
ping) echo_x "pong" ;;
l) cmd_list0 "${cmd[@]:1}" ;;
list) cmd_list "${cmd[@]:1}" ;;
--send|--broadcast) cmd_terminate_or_send "${cmd[@]}" ;;
quit|q) mloop=0 ;;
*) echo "Error: Function '$cmd' not implemented yet."
esac
done
echo_x "NX> 2999 admin mode stopped"
trap SIGINT; trap SIGKILL; trap EXIT; exit_proc 0
} # admin mode end
cmd=$1
echo "NX> 100 NXSERVER - Version $NX_VERSION $NX_LICENSE"
case $cmd in
--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_or_send "$@"
;;
--cleanup)
cmd_terminate_or_send "--force-terminate" "*"
;;
--send|--broadcast)
cmd_terminate_or_send "$@"
;;
*)
cmd_abort "Error: Function $cmd not implemented yet."
esac
echo "NX> 999 Bye"
exit_proc 0