1516 lines
46 KiB
Bash
Executable File
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
|