#!/bin/bash # Free implementation of nxserver components # Copyright (c) 2004 by Fabian Franz. # (c) 2008-23 by Dmitry Borisov # License: GNU GPL, version 2 shopt -s extglob SHARED_CONFS="/usr/share/freenx-server" . $SHARED_CONFS/nxfuncs [ "$1" = "--admin" ] && { # simple wrapper to start nxserver in admin mode if stringinstring " nxadmin " " $(groups) "; then /usr/bin/sudo -p "" /bin/bash -c '/usr/bin/nxserver --admin' 2>&1 exit $? else echo "NX> 2004 admin mode start failed" exit 1 fi } # # ----------------------------------------------------------------------------- # Various helper functions # ----------------------------------------------------------------------------- # nxlog() { [ "$NX_LOG_LEVEL" != "0" ] || return echo "$(date "+%T.%3N"): ${@/password=+([^&])&/password=*&}" >> "$nxuser_logfile" } cp_conv() { # arg: # Used config vars: $COMMAND_ICONV $WIN_CP_CONVERT_CHAIN # successively convert string charset #local lp="$FUNCNAME ($$/$BASHPID):"; #debug #nxlog "$lp starting with args \"$@\"" #debug local res=${1//+/ } cp_pair cp_from cp_to; [ -n "$COMMAND_ICONV" ] || { echo "$res"; return 1; } for cp_pair in $WIN_CP_CONVERT_CHAIN ; do cp_from=$(cutfn "$cp_pair" 0 '>'); [ -n "$cp_from" ] || cp_from="latin1" cp_to=$(cutfn "$cp_pair" 1 '>'); [ -n "$cp_to" ] || cp_to="UTF-8" res=$(echo "$res" | $COMMAND_ICONV -f $cp_from -t $cp_to) #nxlog "$lp converting $cp_from > $cp_to == \"$res\"" #debug done #nxlog "$lp return res='$res'" #debug echo "$res" } # =================== sqlite3 stuff ===================== declare -g sqcols_usess="session_id, status, display, type, client,\ agent_pid, cookie, tail_pid, userip, acc_ip, mmport, cupsport, smbport" declare -g sqcols_usvcs="svc, type, status, port, share, username, pass,\ data, comp, addr" #svc: internal service name #type: smb-share | smb-prn | ipp-prn | media-pa | ... #status: starting|on|stopping|off #port: tunneled/remote port #share: incomming share name #data: options depended of service type eg mount dir... #comp: remote computer name #addr: ip address is 127.0.0.1 typically, another if remote connection used init_usessions_db() { local usess_cols="session_id TEXT PRIMARY KEY, status TEXT, display TEXT,\ type TEXT, client TEXT, agent_pid INT, cookie TEXT, tail_pid INT,\ userip TEXT, acc_ip TEXT, mmport INT, cupsport INT, smbport INT" local svcs_cols="svc TEXT PRIMARY KEY, type TEXT, status TEXT, port INT,\ share TEXT, comp TEXT, addr TEXT, username TEXT, pass TEXT, data TEXT" local qstr="CREATE TABLE IF NOT EXISTS usessions.usess($usess_cols);" qstr+="CREATE TABLE IF NOT EXISTS usessions.usvcs($svcs_cols);" q_dbe "$qstr" } # ------------------ user session (usess) control -------- usess_add() { q_row_ins "usess" "$1" "$2"; } #usess_add() args: usess_set() { q_rows_upd "usess" "session_id='$1'" "$2" "$3"; } #usess_set() args: usess_get() { q_vals_str_get "usess" "session_id='$1'" "$2" "$3"; } #usess_get() args: [values_delim='&'] usess_close() { #args: if [ "$SESSION_LOG_CLEAN" = "1" ]; then q_dbe "DELETE FROM usess WHERE session_id='$1';" else q_rows_upd "usess" "session_id='$1'" "status" "$2" fi } # -------------- user services (uservices) control -------- #usvcs_add() args: usvcs_add() { q_row_ins "usvcs" "$1" "$2"; } #usvcs_set() args: usvcs_set() { q_rows_upd "usvcs" "svc='$1'" "$2" "$3"; } #usvcs_get() args: [values_delim='&'] usvcs_get() { q_vals_str_get "usvcs" "svc='$1'" "$2" "$3"; } # # ----------------------------------------------------------------------------- # Node functions module # ----------------------------------------------------------------------------- # sess_lport_name() { #arg: case $1 in smb-share|smb-prn) echo "smbport";; ipp-prn) echo "cupsport";; media-pa) echo "mmport";; esac } norm_dir() { #args: [parent_dir] # exclude potential parts to exec and set dir path from given parent_dir local r=${1//\`/}; r=${r//\$[(\{]*[)\}]/$2}; r=${r/\.\.\//$2\/}; r=${r/\.\//$2\/}; r=${r/\~\//$2\/} [[ "${r:0:1}" =~ [[:alnum:]] ]] && r="$2/$r" echo "$r" } uservice_mounted() { #args: [port] local rc=0 txt="" pattern port="" local patt_addr="127.0.0.1"; [ -n "$4" ] && patt_addr=$4 case $1 in smb-share) # output of mount cmd not contains mount.cifs port option value # because we need to port arg ($3) txt=$(env LC_ALL=C $COMMAND_MOUNT_LIST 2>/dev/null) if stringinstring "//" "$2"; then pattern="($2)" elif ! stringinstring "/" "$2"; then pattern="//$patt_addr/($2)" else pattern="($2)" fi [ -n "$(rematchfn "$pattern" "$txt")" ] || return 1 if [ -n "$3" ]; then port_is_listening $3 || rc=1 fi ;; smb-prn|ipp-prn) txt=$(env LC_ALL=C $COMMAND_LPSTAT -v 2>/dev/null) pattern=".*$2.*//$patt_addr:(" [ -n "$3" ] && pattern+="$3)" || pattern+="$num_pattern)" port=$(rematchfn "$pattern" "$txt"); rc=$? [ -n "$port" ] || return $rc port_is_listening "$port" || rc=1 ;; media-pa) case $2 in pa) # tunneled pa #$COMMAND_PA --check || return 1 txt=$(env LC_ALL=C $COMMAND_PACTL list short 2>/dev/null) pattern="server=$patt_addr:(" [ -n "$3" ] && pattern+="$3)" || pattern+="[0-9]+)" port=$(rematchfn "$pattern" "$txt"); rc=$? #nxlog "$1 port=$port" #debug [ -n "$port" ] || return $rc port_is_listening "$port" || rc=1 ;; esac ;; esac return $rc } uservice_configure() { #args: # smb-share # *-prn # opts="model=;public=;defaultprinter=" # media-pa <""> <""> local lp="$FUNCNAME ($$/$BASHPID):"; local cmdstr optstr comp rc=0 txt errstr uri case $1 in smb-share) # create/check mountpoint mkdir -p "$6" &>/dev/null [ -d "$6" ] || { nxlog "$lp unable to create dir='$6'"; return 1; } local egroup=$(id -gn "$USER") tmpopts=${SMB_MOUNT_OPTIONS//,/&} local dir_mode=$(getparam "$tmpopts" dir_mode) dir_mode=${dir_mode:(-3)}; [ -n "$dir_mode" ] || dir_mode="700" [ "$(stat -c %a "$6")" != "$dir_mode" ] && chown "0$dir_mode" "$6" &>/dev/null # mount options string optstr="uid=$USER,gid=$egroup,ip=127.0.0.1,port=$3,username=$4" [ -n "$5" ] && optstr+=",password=$5" [ -n "$SMB_MOUNT_OPTIONS" ] && optstr+=",$SMB_MOUNT_OPTIONS" cmdstr="$COMMAND_SUDO $COMMAND_SMBMOUNT $2 $6 -o $optstr" echo "$cmdstr" ;; smb-prn|ipp-prn) local model=$(getparam "$6" "model" "" ';') [ "$model" = "NULL" ] && model="" local ppdn=${2#$USER}; ppdn=${ppdn:1}; ppdn=${ppdn%#[0-9]*} local ppdfn="$NX_PPD_DIR/$ppdn.ppd" [ -r "$ppdfn" ] || { # ppd is not found, search for driver in CUPS txt=$($COMMAND_LPINFO -m 2>/dev/null) local str drv="" [ -n "$model" ] && { while read str; do [[ "$str" =~ "$model" ]] && { drv="${str%% *}"; break; } done <<< "$txt" } [ -n "$drv" ] || { while read str; do [[ "$str" =~ "$ppdn" ]] && { drv="${str%% *}"; break; } done <<< "$txt" } [ -n "$drv" ] || { nxlog "$lp '$svc'; CUPS driver for service is not found"; return 1; } $COMMAND_PPDCAT cat "$drv" > "$ppdfn" || { nxlog "$lp Can't save $ppdfn"; return 1; } } [ "${1:0:3}" = "smb" ] && \ uri="nxsmb://$4:$5@127.0.0.1:$3/cifs/$8" || \ uri="ipp://$4:$5@127.0.0.1:$3/printers/$8" cmdstr="$COMMAND_SUDO $COMMAND_LPADMIN -p $svc -P $ppdfn -v $uri -E" echo "$cmdstr" ;; media-pa) case $2 in pa) # tunneled pa local uri="127.0.0.1:$3" # get sink and source from remote pa local rmods=$(env LC_ALL=C $COMMAND_PACTL -s $uri list short 2>/dev/null) [ -n "$rmods" ] || { nxlog "$lp '$svc'; can't get module list from remote PA ($uri)"; return 1; } local rsink=$(rematchfn "(ts_receiver)" "$rmods") #" local rsource=$(rematchfn "(ts_sender.monitor)" "$rmods") #" [ -n "$rsink" -a -n "$rsource" ] || { local rinfo=$(env LC_ALL=C $COMMAND_PACTL -s $uri info 2>/dev/null) [ -n "$rsink" ] || \ rsink=$(rematchfn "Default Sink:[[:blank:]]+(.+)" "$rinfo") #" [ -n "$rsource" ] || \ rsource=$(rematchfn "Default Source:[[:blank:]]+(.+)" "$rinfo") #" } echo "$rsink $rsource" ;; esac ;; esac return $rc } uservice_mount() { #args: # smb-share # *-prn # media-pa "" "" local lp="$FUNCNAME ($$/$BASHPID):" rc=0 cmdstr errstr local i ok="" step=0.25 timeo=28 #7sec for (( i=0; i<=timeo; i++ )); do port_is_listening $3 && { ok="1"; break; } sleep $step"s" done [ -n "$ok" ] || \ { nxlog "$lp '$svc'; port $3 is not listen after $((timeo/4))s"; return 1; } cmdstr=$(uservice_configure "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8") || return 1 case $1 in smb-share) #nxlog "$lp share cmdstr='$cmdstr'" #debug errstr=$($cmdstr 2>&1) || \ { nxlog "$lp $2 ($3) share mount failed: $errstr" rmdir "$6" &>/dev/null; return 1; } nxlog "$lp $2 ($3) share mounted" ;; smb-prn|ipp-prn) #nxlog "$lp add printer cmdstr='$cmdstr'" #debug errstr=$($cmdstr 2>&1) || \ { nxlog "$lp $2 ($3) printer installing failed: $errstr"; return 1; } # post-configure local public=$(getparam "$6" "public" "" ';') [ "$public" != "1" ] && { errstr=$($COMMAND_SUDO $COMMAND_LPADMIN -p $svc -u allow:$USER,guest,root 2>&1) || \ { nxlog "$lp $2 ($3) printer set permission failed: $errstr"; } } local defp=$(getparam "$6" "defaultprinter" "" ';') [ "$defp" = "1" ] && { errstr=$($COMMAND_SUDO $COMMAND_LPADMIN -d $svc 2>&1) || \ { nxlog "$lp $2 ($3) printer set to default failed: $errstr"; } } nxlog "$lp $2 ($3) printer installed" ;; media-pa) if ! $COMMAND_PA --check &>/dev/null; then $COMMAND_PA --start --exit-idle-time=-1 &>/dev/null || { #--log-target=file:$nx_dir/pa-$3.log --log-level=4 || { #debug nxlog "$lp '$svc' can't start local pulseaudio server"; return 1; } # automatic null-sink will be disabled # unload unnecessary local modules here too local rmmods="module-always-sink module-rescue-streams module-systemd-login \ module-device-restore module-stream-restore module-card-restore \ module-default-device-restore module-switch-on-port-available \ module-udev-detect module-suspend-on-idle module-console-kit" local txt=$($COMMAND_PACTL list short 2>/dev/null) local midpat="([0-9]+)[[:blank:]]+" mid rmod for rmod in $rmmods; do mid=$(rematchfn "$midpat$rmod" "$txt") && { $COMMAND_PACTL unload-module $mid &>/dev/null #nxlog "$lp ! $mid" #debug } done else nxlog "$lp '$svc' local pulseaudio server already started" #debug fi case $2 in pa) # tunneled pa local rsink=$(cutfn "$cmdstr" 0) rsource=$(cutfn "$cmdstr" 1) local mname="module-tunnel-sink" opts="server=127.0.0.1:$3" oo args ok2="" oo=$(cutfn "$6" 1 '-'); [ -n "$oo" ] && opts+=" rate=$oo" oo=$(cutfn "$6" 2 '-'); [ -n "$oo" ] && opts+=" channels=$oo" [ "$oo" = "1" ] && opts+=" channel_map=mono" if [ "$rsink" != "(null)" ]; then args="sink_name=tcl_out sink=$rsink $opts" errstr=$($COMMAND_PACTL load-module $mname $args 2>&1) [ $? -eq 0 ] && ok="tcl_out" || \ nxlog "$FUNCNAME ($$): $2 ($3) can't load $mname $args; '$errstr'" [ -n "$ok" ] && $COMMAND_PACTL set-default-sink "tcl_out" &>/dev/null else nxlog "$lp $2 ($3) can't load $name with rsink=$rsink" fi mname="module-tunnel-source" if [ "$rsource" != "(null)" ]; then args="source_name=tcl_in source=$rsource $opts" errstr=$($COMMAND_PACTL load-module $mname $args 2>&1) [ $? -eq 0 ] && ok2="tcl_in" || \ nxlog "$FUNCNAME ($$): $2 ($3) can't load $mname $args; '$errstr'" [ -n "$ok2" ] && $COMMAND_PACTL set-default-source "tcl_in" &>/dev/null else nxlog "$lp $2 ($3) can't load $name with rsource=$rsource" fi if [ -n "$ok" -o -n "$ok2" ]; then nxlog "$lp $2 ($3) tunnel modules loaded: $ok $ok2" else rc=1 fi ;; esac ;; esac return $rc } uservice_umount() { #args: [data] [port] local lp="$FUNCNAME ($$/$BASHPID):" errstr="" local i ok="" step="0.5" ct=4 case $1 in smb-share) local mdir=$3 res ffl="" if stringinstring "//" "$2"; then res="$2" # svc elif ! stringinstring "/" "$2"; then res="//127.0.0.1/$2" # sharename else mdir="$2" res="$2" # dir fi #[ -z "$mdir" ] && { # get mount dir directly # local txt=$(LC_ALL=C mount 2>/dev/null) # pattern="$res""[[:blank:]]+on[[:blank:]]+([^[:blank:]]+)" # mdir=$(remathfn "$pattern" "$txt") # nxlog "$lp share $res: given empty mount dir, loaded mdir='$mdir' " #} for (( i=1; i<=ct; i++ )); do (( i>=ct/2 )) && ffl="-f" errstr+=$($COMMAND_SUDO $COMMAND_SMBUMOUNT $ffl "$res" 2>&1) && \ { ok="1"; break; } sleep $step"s" done [ -n "$ok" ] || \ { nxlog "$lp $res share umount failed: $errstr"; return 1; } [ -n "$mdir" ] && [ -d "$mdir" ] && rmdir "$mdir" &>/dev/null nxlog "$lp $res share umounted" ;; smb-prn|ipp-prn) for (( i=1; i<=ct; i++ )); do errstr+=$($COMMAND_SUDO $COMMAND_LPADMIN -x "$2" 2>&1) && \ { ok="1"; break; } sleep $step"s" done [ -n "$ok" ] || \ { nxlog "$lp $2 printer deleting failed: $errstr"; return 1; } nxlog "$lp $2 printer deleted" ;; media-pa) case $2 in pa) # tunneled pa local midpat="([0-9]+)[[:blank:]]+module-" uri="127.0.0.1" [ -n "$4" ] && uri+=":$4" txt=$(env LC_ALL=C $COMMAND_PACTL list short 2>/dev/null) [ -n "$txt" ] || { nxlog "$lp '$svc'; local PA already stopped"; return 0; } local mid es mids=$(rematchfn "$midpat.+server=$uri" "$txt" all); #nxlog "$lp '$2'; rmids: "$mids; #debug for mid in $mids; do es=$($COMMAND_PACTL unload-module $mid 2>&1) [ $? -ne 0 ] && errstr+="\n$es" done [ -n "$errstr" ] && { nxlog "$lp '$2'; unload some local PA modules failed: $errstr"; } nxlog "$lp $2 remote tunnel disconnected" #nxlog "$lp $2 remote tunnel: $($COMMAND_PACTL list short 2>/dev/null)" #debug ;; esac [ -n "$3" ] && { $COMMAND_PA --kill || { nxlog "$lp '$2'; unable to kill local PA"; return 0; } } ;; esac return 0 } uservice_start() { #args: # [port] [type] [sharename] [username] [password] [data] [comp] # [addr=127.0.0.1] # if type is empty try to operate params from usess db # if port not empty try to start on him # Used config vars: COMMAND_HIDE COMMAND_UNHIDE local lp="$FUNCNAME ($$/$BASHPID):" errstr="" qs svcport local st startfl="" checkfl="" updvars="" updvals="" hpass local svc="$1" port="$2" type="$3" share="$4" local username="$5" pass="$6" data="$7" comp="$8" local addr="$9"; [ -z "$addr" ] && addr="127.0.0.1"; [ "$type" = "smb-share" -a -n "$data" ] && data=$(norm_dir "$data" $HOME) local i ok="" step="0.25" timeo="28" #4sec [ -n "$type" -a -z "$port" ] && { # starting no restarting - we wait for session listening port just in case local lport_name=$(sess_lport_name $type) local wstr="session_id='$session_id' AND $lport_name>0" for (( i=0; i<=timeo; i++ )); do port=$(q_vals_str_get "usess" "$wstr" "$lport_name") && break sleep $step"s" done [ -n "$port" ] || { nxlog "$lp $svc session $lport_name no declared after $((timeo/4)) s"; return 1; } } # waiting for suitable service status: on/off/"" ok="" step="0.25" timeo="28" #7sec for (( i=0; i<=timeo; i++ )); do st=$(usvcs_get $svc "status") || { ok="1"; break; } stringinstring "$st" "starting|stopping" || { ok="1"; break; } sleep $step"s" done [ -n "$ok" ] || { nxlog "$lp service $svc ($port) still set in \"$st\" state after $((timeo/4)) s"; # FIXME! [ "$st" = "stopping" ] || return 1; nxlog "$lp service $svc ($port) set state 'off' ultimately"; usvcs_set $svc "status" "off"; st="off" } [ -z "$type" -a -z "$st" ] && { nxlog "$lp '$svc': params for service are not found in usess db"; return 1; } [ -z "$type" ] && { #load params from usess db qs="$(usvcs_get $svc "type,port,share,username,pass,data,comp,addr")" || { nxlog "$lp '$svc': can't get service params from usess db"; return 1; } type=$(cutfn "$qs" 0 '&'); [ -z "$port" ] && port=$(cutfn "$qs" 1 '&'); share=$(cutfn "$qs" 2 '&'); username=$(cutfn "$qs" 3 '&'); pass=$(cutfn "$qs" 4 '&'); pass=$(echo "$pass" | $COMMAND_UNHIDE); data=$(cutfn "$qs" 5 '&'); comp=$(cutfn "$qs" 6 '&'); addr=$(cutfn "$qs" 7 '&'); #nxlog "$lp service $svc ($st) load qs='$qs'" #debug } if [ "$st" = "on" ]; then if uservice_mounted $type $svc; then nxlog "$lp service $svc is allready mounted, skipping"; return 0 else nxlog "$lp $svc service status is \"$st\", but it's not mounted. Try to start again"; startfl=1; [ -n "$type" ] && checkfl=1; st="starting"; usvcs_set $svc "status" $st fi else if uservice_mounted $type $svc; then usvcs_set $svc "status" "stopping" nxlog "$lp $svc service status is \"$st\", but it's mounted. Try to stop"; uservice_umount $type $svc $data || { return 1; } fi if [ "$st" = "off" ]; then startfl=1; [ -n "$type" ] && checkfl=1; st="starting"; usvcs_set $svc "status" $st else # svc is not found in usess table hpass=$(echo $pass | $COMMAND_HIDE) usvcs_add "svc,type,status,port,share,comp,addr,username,pass,data" \ "$svc&$type&starting&$port&$share&$comp&$addr&$username&$hpass&$data" startfl=1; fi fi [ -n "$startfl" ] && { [ -n "$checkfl" ] && { local s_share s_username s_pass s_data s_comp s_addr qs="$(usvcs_get $svc "share,username,pass,data,comp,addr")" || { nxlog "$lp can't get service $svc params from usess _db"; return 1; } s_share=$(cutfn "$qs" 0 '&'); s_username=$(cutfn "$qs" 1 '&'); s_pass=$(cutfn "$qs" 2 '&'); s_pass=$(echo $s_pass | $COMMAND_UNHIDE); s_data=$(cutfn "$qs" 3 '&'); s_comp=$(cutfn "$qs" 4 '&'); s_addr=$(cutfn "$qs" 5 '&'); [ "$share" != "$s_share" ] && { nxlog "$lp $svc share strings are different '$s_share' > '$share'" updvars+=",share"; updvals+="&$share"; } [ "$username" != "$s_username" ] && { nxlog "$lp $svc username strings are different '$s_username' > '$username'" updvars+=",username"; updvals+="&$username"; } [ "$pass" != "$s_pass" ] && { nxlog "$lp $svc password strings are different" hpass=$(echo $pass | $COMMAND_HIDE) updvars+=",pass"; updvals+="&$hpass"; } [ "$data" != "$s_data" ] && { nxlog "$lp $svc share strings are different '$s_data' > '$data'" updvars+=",data"; updvals+="&$data"; } [ "$comp" != "$s_comp" ] && { nxlog "$lp $svc comp strings are different '$s_comp' > '$comp'" updvars+=",comp"; updvals+="&$comp"; } [ "$addr" != "$s_addr" ] && { nxlog "$lp $svc addr strings are different '$s_addr' > '$addr'" updvars+=",addr"; updvals+="&$addr"; } } #nxlog "$lp _$st _$type _$svc _$port _$username _$pass _$data _$comp" _$share" #debug if uservice_mount $type $svc $port "$(echo -e "${username//\%/\\x}")" \ "$pass" "$data" "$(echo -e "${comp//\%/\\x}")" "$share"; then usvcs_set $svc "status,port""$updvars" "on&"$port$updvals return 0 else usvcs_set $svc "status,port" "off&0" fi } return 1 } uservice_stop() { #arg: svc [type] [norestart] # Used gvars: session_id local lp="$FUNCNAME ($$/$BASHPID):" local i ok="" st type=$2 lport_name lport="" data="" hardstop=$3 # waiting for suitable service status: on local step=0.25 timeo=28 #7sec for (( i=0; i<=timeo; i++ )); do st=$(usvcs_get $svc "status") [ "$st" = "on" ] && { ok="1"; break; } sleep $step"s" done [ -n "$ok" ] || { nxlog "$lp service $svc still set in \"$st\" state after $((timeo/4))s waitng"; return 1; } [ -z "$type" ] && type=$(usvcs_get $svc "type") usvcs_set $svc "status" "stopping" [ -n "$hardstop" ] || { # get suitable session first lport_name=$(sess_lport_name $type) local wstr="status='Running' AND session_id!='$session_id' AND $lport_name>0" #nxlog "$lp $svc wstr='$wstr'" #debug lport=$(q_vals_str_get "usess" "$wstr" "$lport_name") || { hardstop=1 nxlog "$lp '$svc': other suitable listening port is not found" #; wstr='$wstr'" #debug } } [ -n "$hardstop" ] && { # if unable to restart we must known data data=$(usvcs_get $svc "data"); } #nxlog "$lp '$svc': hs=$hardstop; data=$data" #debug uservice_umount $type $svc "$data" "$port" || return 1 [ -n "$hardstop" ] && { if [ "$SESSION_LOG_CLEAN" = "1" ]; then q_dbe "DELETE FROM usvcs WHERE svc='$1';" else usvcs_set $1 "status,port" "off&0" fi return 0; } usvcs_set $svc "status,port" "off&0" uservice_start $svc $lport #|| return 1 return 0 } node_stop_services() { # Used gvars: session_id #local lp="$FUNCNAME ($$/$BASHPID):" local lports=",$(usess_get $session_id "smbport,cupsport,mmport" ',')" lports=${lports//,0/}; lports=${lports:1} [ -n "$lports" ] || return # no services in session local svc svcs svcs=$(q_vals_strs_get "usvcs" "status='on' AND port IN ($lports)" "svc") #" #nxlog "$lp lports=($lports) services list to stop: '$svcs'"; #debug for svc in $svcs; do uservice_stop $svc; done } node_terminate_session() { #args: [status] # Used gvars: nx_dir # Used config vars: COMMAND_XAUTH, SESSION_LOG_CLEAN SERVER_NAME local lp="$FUNCNAME ($$/$BASHPID):"; #nxlog "$lp Start terminating session_id='$1' with status '$2'" #debug local qs=$(usess_get "$1" "display,agent_pid,tail_pid,status") [ -z "$qs" ] && { nxlog "$lp session_id='$1' not found in usess db. Bye."; return; } local status=$(cutfn "$qs" 3 '&') stringinstring "$status" "Terminating|Finished|Failed" && { nxlog "$lp Session status is already '$status'. Bye."; return; } local display=$(cutfn "$qs" 0 '&'); local sess_id="$SERVER_NAME-$display-$1" [ -d "$nx_dir/C-$sess_id" ] || { nxlog "$lp Session dir '$nx_dir/C-$sess_id' not found. Bye."; return; } usess_set "$1" "status" "Terminating" local agent_pid=$(cutfn "$qs" 1 '&') tail_pid=$(cutfn "$qs" 2 '&') local t_status="$2"; [ -z "$2" ] && t_status="Finished" node_stop_services kill -0 $agent_pid 2>/dev/null && { #nxlog "$lp start killing of nxagent ($agent_pid)" #debug kill $agent_pid 2>/dev/null wait $agent_pid 2>/dev/null kill -0 $agent_pid 2>/dev/null || nxlog "$lp nxagent ($agent_pid) is dead now" } ((tail_pid>0)) && kill -0 $tail_pid 2>/dev/null && { # Kill tail process #nxlog "$lp kill tail process ($tail_pid)" #debug kill $tail_pid 2>/dev/null wait $tail_pid 2>/dev/null kill -0 $tail_pid 2>/dev/null || { nxlog "$lp tail ($tail_pid) is dead now"; tail_pid="0" usess_set "$session_id" "tail_pid" "0" } } #nxlog "$lp Remove session information" #debug rm -f /tmp/.X$display-lock; rm -f /tmp/.X11-unix/X$display # Remove magic cookie information $COMMAND_XAUTH remove "localhost:$display" >/dev/null 2>&1 $COMMAND_XAUTH remove ":$display" >/dev/null 2>&1 if [ "$SESSION_LOG_CLEAN" = "1" ]; then #nxlog "$lp Clean session information." #debug rm -rf "$nx_dir/C-$sess_id/" rm -f "$nx_dir/nxnode-$1.log" rm -f "$nx_dir/nxnode.log" elif [ "$2" = "Failed" ]; then mv "$nx_dir/C-$sess_id/" "$nx_dir/F-C-$sess_id" else mv "$nx_dir/C-$sess_id/" "$nx_dir/T-C-$sess_id" fi usess_close "$1" "$t_status" #nxlog "$lp end" #debug } node_fail_restore_session() { #arg: #local lp="$FUNCNAME ($$/$BASHPID):"; nxlog "$lp starting" #debug echo "NX> 1004 Error: Could not resume session. nxagent process could not be found." node_terminate_session "$1" "Failed" #nxlog "$lp end. Next is 'exit 1'" #debug exit_proc 1 } node_suspend_session() { #arg: local lp="$FUNCNAME ($$/$BASHPID):"; #nxlog "$lp starting" #debug local agent_pid=$(usess_get "$1" "agent_pid") nxlog "$lp Killing (HUP) agent_pid ($agent_pid)..." kill -0 $agent_pid 2>/dev/null || { nxlog "$lp nxagent is already dead. end (1)"; return 1; } kill -HUP $agent_pid 2>/dev/null && { nxlog "$lp end (HUP)"; return 0; } return 1 } node_find_application() { #args: # Used config vars: $COMMAND_START_KDE, $COMMAND_START_GNOME, # $COMMAND_START_CDE, $COMMAND_XTERM, $USER_X_STARTUP_SCRIPT,$DEFAULT_X_SESSION local lp="$FUNCNAME ($$/$BASHPID):"; #nxlog "$lp starting with args \"$@\"" #debug local node_startx="" case $1 in shadow|windows|vnc) return ;; unix-kde) node_startx=$COMMAND_START_KDE ;; unix-gnome) node_startx=$COMMAND_START_GNOME ;; unix-cde) node_startx=$COMMAND_START_CDE ;; windows-helper) node_startx="$COMMAND_RDESKTOP /v:$agent_server /u:$agent_user" [ -n "$agent_domain" ] && node_startx+=" /d:$agent_domain" node_startx+=" /t:NX-$session-RDP-$agent_server@$agent_user" node_startx+=" /p:$agent_password /size:$geometry $EXTRA_OPTIONS_RDP" ;; vnc-helper) if [ ! -x "$COMMAND_VNCVIEWER" ]; then echo "$COMMAND_XMSG 'vncviwer not found'" else mkdir -p "$NXSESSION_DIRECTORY/scripts/" echo "$agent_password" | \ $COMMAND_VNCPASSWD $NXSESSION_DIRECTORY/scripts/.passwd doit node_startx="$COMMAND_VNCVIEWER -passwd \ $NXSESSION_DIRECTORY/scripts/.passwd $EXTRA_OPTIONS_RFB $agent_server" fi ;; unix-application) [ "$application" = "xterm" ] && application=$COMMAND_XTERM node_startx=$application ;; unix-console) node_startx=$COMMAND_XTERM ;; unix-default|*) if [ -x "$HOME/$USER_X_STARTUP_SCRIPT" ]; then node_startx="$HOME/$USER_X_STARTUP_SCRIPT" elif [ -x "$DEFAULT_X_SESSION" ]; then node_startx="$DEFAULT_X_SESSION" else node_startx=$COMMAND_XTERM fi ;; esac # dimbor: another personalyzed way to ACLS control and replace X-application [ -n "$NX_ACL_DIR" ] || { echo "$node_startx"; return; } local ucond=$(groups); ucond="'#$USER','*${ucond// /\',\'\*}','@all'" local mode=".mode csv settings\n.separator '&'\n" local qstr="SELECT key,value,val_depend FROM settings WHERE user IN ($ucond) \ AND key NOT IN ('@shadow@') ORDER BY user ASC, val_check ASC;" #nxlog "$lp qstr='$qstr'" #debug local ts=$(qa_dbe "$mode" "$qstr") #nxlog "$lp ts='$ts'" #debug local l tpl inv pm l2="" app_match="" app_rep="$COMMAND_XMSG '$NX_ACL_WARN'" while read l; do tpl="${l%%&*}"; tpl="$(sq2s "$tpl")" inv=0; [ "${tpl:0:1}" = "!" ] && { tpl="${tpl:1}"; inv=1; } pm=0; [[ "$node_startx" =~ $tpl ]] && pm=1 #nxlog "$lp $node_startx -> \"$l\" -> $tpl $inv $pm" #debug ((inv+pm==1)) && { l2=$l; app_match=1; break; } done <<< "$ts" if [ -n "$app_match" ]; then tpl=$(cutfn "$l2" 1 '&'); tpl=$(sq2s "$tpl") [ -n "$tpl" ] && { # process list checking app_match="" inv=0; [ "${tpl:0:1}" = "!" ] && { tpl="${tpl:1}"; inv=1; } local psall=""; [ "${tpl:0:2}" = "@@" ] && { tpl="${tpl:2}"; psall=1; } local psl; if [ -n "$psall" ]; then psl="$(ps ax -o cmd=)" else psl="$(ps -o cmd= -U $USER)" fi pm=0; rematchfn "($tpl)" "$psl" &>/dev/null && pm=1 ((inv+pm==1)) && app_match=1 #nxlog "$lp $l - $tpl $inv $pm" #debug } local msg=$(cutfn "$l2" 2 '&'); msg=$(sq2s "$msg") [ -n "$msg" ] && { # exe/msg checking # need absolute path here? [ -x "${msg%% *}" ] && app_rep="$msg" || app_rep="$COMMAND_XMSG '$msg'" } fi if [ -n "$app_match" ]; then echo "$node_startx"; else nxlog "$lp App '$node_startx' replaced to '$app_rep'" echo "$app_rep" fi } node_start_applications() { # Used glob vars: $type, $application, $sess_id, $mediahelper, # $virtualdesktop, $rootless, $display # Used config vars: local lp="$FUNCNAME ($$/$BASHPID):"; #nxlog "$lp starting" #debug local app node_app napp; local slave_apps psess sess_ps sapp; local node_app_pid node_wm_pid; local ok step timeo local q=0 i s l # Prepare application startup export DISPLAY=:$display #nxlog "$lp display='$display', waiting for it's ready" #debug ok="" step="0.01" timeo=$((AGENT_STARTUP_TIMEOUT*100)) for (( i=0; i<=timeo; i++ )); do [ -f /tmp/.X$display-lock ] && { ok="1"; break; } sleep $step"s" done [ -n "$ok" ] || { nxlog "$lp /tmp/.X$display-lock not found after $AGENT_STARTUP_TIMEOUT s"; return 1; } #numlockx if [ "$NUMLOCK_METHOD" != "system" ]; then #nxlog "$lp Run \"$NUMLOCKX $NUMLOCKX_STATUS\"" #debug "$NUMLOCKX" "$NUMLOCKX_STATUS" fi # Which application do we start? app=$(node_find_application "$type") # For rdesktop/VNC, there is no application to start [ -n "$app" ] || return if [ "$ENABLE_SAMBA_PRELOAD" = "1" ]; then NXSAMBA_PORT=$(usess_get "$session_id" "smbport") ((NXSAMBA_PORT>0)) && { export NXSAMBA_PORT #nxlog "$lp Preload SAMBA using nxredir. NXSAMBA_PORT is '$NXSAMBA_PORT'" #debug node_app="$PATH_BIN/nxredir $node_app" echo "Info: NXNODE - Using nxredir wrapper script to forward \ SMB ports 139 and 445 to port $NXSAMBA_PORT." >> "$NXSESSION_DIRECTORY/session" } fi #nxlog "$lp app is $app" #debug # do fake eval for params q=0; l=$app; node_app=() while [ -n "$l" ]; do s=${l%%[\'\"]*}; l=${l#*[\'\"]} [ "$s" = "$l" ] && l="" [ -n "$s" ] || continue ((q==0)) && { node_app+=($s); q=1; } || { node_app+=("$s"); q=0; } done #nxlog "$lp ${#node_app[@]}" #debug #for ((i=0; i<${#node_app[@]}; i++)); do nxlog "$lp ${node_app[$i]}"; done #debug [ "$cups" = "1" -o "$samba" = "1" ] && { #nxlog "$lp export CUPS_SERVER=$CUPS_DEFAULT_SOCK" #debug export CUPS_SERVER=$CUPS_DEFAULT_SOCK } # Use Xsession to execute the Desktop session case $type in unix-gnome) export STARTUP="${node_app[@]}" [ "$BOOTSTRAP_X_SESSION" = "1" ] && node_app=($COMMAND_GDM_X_SESSION) ;; unix-kde|unix-cde) export STARTUP="${node_app[@]}" [ "$BOOTSTRAP_X_SESSION" = "1" ] && node_app=($DEFAULT_X_SESSION) ;; esac [ $ENABLE_ROOTLESS_TERMINATE_SESSION = "1" -a "$rootless" = "1" ] && { psess=$($COMMAND_PS -wo sess= -p $$) napp=${node_app[0]}; napp=${napp##*/} slave_apps=$(rematchfn "$napp:(.+)" "${APP_WAIT_MAP//;/$'\n'}") #" #nxlog "$lp slave(s) will be waiting too. Initial apps: '$slave_apps'" #debug } # Do we need to PRELOAD any libraries? [ "$virtualdesktop" = "0" -a "$rootless" != "1" ] && export LD_PRELOAD="$APPLICATION_LIBRARY_PRELOAD:$LD_PRELOAD" # close input and output file descriptors exec 0<&-; exec 1>&-; exec 2>&- # Should we start a window manager? if [ "$virtualdesktop" = "1" -a "$type" = "unix-application" -a \ -x "$DEFAULT_X_WM" ]; then #nxlog "$lp start a window manager - \"DISPLAY=:$display $DEFAULT_X_WM\"" #debug DISPLAY=:$display $DEFAULT_X_WM >>"$NXSESSION_DIRECTORY/session" 2>&1 & node_wm_pid=$! #nxlog "$lp node_wm_pid='$node_wm_pid'" #debug fi # Startup the application #nxlog "$lp Starting node_app with /etc/nxserver/Xsession" #debug DISPLAY=:$display /etc/nxserver/Xsession "${node_app[@]}" >> \ "$NXSESSION_DIRECTORY/session" 2>&1 & node_app_pid=$! nxlog "$lp Start '${node_app[@]}'. Waiting for node_app_pid='$node_app_pid'" wait $node_app_pid nxlog "$lp node_app_pid finished" # Kill or wait for the started window manager [ -n "$node_wm_pid" ] && { nxlog "$lp node_wm_pid is not empty" # kill the WM after application is finished? [ "$KILL_DEFAULT_X_WM" = "1" ] && { nxlog "$lp killing $node_wm_pid" kill $node_wm_pid 2>/dev/null; } # or just wait until it finishes? [ "$KILL_DEFAULT_X_WM" = "1" ] || { nxlog "$lp wait for $node_wm_pid is dead" wait $node_wm_pid; } } sleep "$NODE_APP_WAIT_TIMEOUT"s [ $ENABLE_ROOTLESS_TERMINATE_SESSION = "1" -a "$rootless" = "1" ] && { if [ -n "$slave_apps" ] ; then nxlog "$lp slave(s) will be waiting too. Initial: '$slave_apps'" sapp=$napp while [ -n "$sapp" ]; do sess_ps=$($COMMAND_PS -wo user=,cmd= -s $psess); sapp="" #nxlog "$lp '$sess_ps'" #debug while read l; do [ "$(cutfn "$l" 0)" = "$USER" ] || continue s=$(cutfn "$l" 1); s=${s##*/} stringinstring "$s," "$slave_apps," && { sapp=$s; #nxlog "$lp >> $sapp '$l'" #debug break; } done <<< "$sess_ps" sleep "$NODE_APP_WAIT_TIMEOUT"s done nxlog "$lp Session app(s) are finished" fi node_terminate_session "$session_id" } # Do not terminate agent in case of rootless agent mode. # The agent times out after a while by itself anyway. if [ "$virtualdesktop" = "1" -o "$rootless" != "1" ] ; then #nxlog "$lp Call node_terminate_session for non-rootless or virtualdesktop session type" #debug node_terminate_session "$session_id" fi #nxlog "$lp end" #debug } node_agent_persistent_session() { # Is the user allowed to run a persistent session? local username IFS="," p="-nopersistent" [ "$ENABLE_PERSISTENT_SESSION" = "all" ] && p="-persistent" || { for username in $ENABLE_PERSISTENT_SESSION; do [ "${username:0:1}" != "@" ] && [ "$USER" = "$username" ] && \ p="-persistent" && break; [ "${username:0:1}" = "@" ] && \ [ -z $(groups "$USER" | egrep "^${username:1}:") ] && \ p="-persistent" && break; done } for username in $DISABLE_PERSISTENT_SESSION; do [ "${username:0:1}" != "@" ] && [ "$USER" = "$username" ] && \ p="-nopersistent" && break; [ "${username:0:1}" = "@" ] && \ [ -z $(groups "$USER" | egrep "^${username:1}:") ] && \ p="-nopersistent" && break; done echo "$p" } node_start_agent() { # Ok, now we do some wicked fd magic. # first part: nxagent's fd #2 -> fd #3 # second part: fd #1 -> #4; fd #3 -> #1; tee | node_start_monitor # third part: fd #4 -> #1 # => all output of nxagent goes to tee | node_start_monitor, while # leaving all other output flow through like normally. # preparations local lp="$FUNCNAME ($$/$BASHPID):"; local k g b r fp vncfullscreen u p d agent_port viewonly local agent_exit_status node_failed #nxlog "$lp starting" #debug exec 3>&2; exec 4>&1; { { export DISPLAY="nx/nx,options=$NXSESSION_DIRECTORY/options:$display" export XAUTHORITY="$NXSESSION_DIRECTORY/authority" export HOME export NX_CLIENT="$PATH_BIN/nxdialog" # Setup optional parameters for nxagent # keyboard k="" # backwards compatibility [ -n "$keyboard" ] && k="-keyboard $keyboard" [ -n "$kbtype" ] && k="-kbtype $kbtype" # backingstore b="" if [ -n "$backingstore" ]; then [ "$backingstore" != 1 ] && b="-bs $backingstore" [ "$backingstore" = 1 ] && b="+bs" fi # geometry g="" [ -n "$geometry" ] && g="-geometry $geometry" # type of session r="-D"; [ "$rootless" = "1" ] && r="-R" # Setup fullscreen parameters [ "$geometry" = "fullscreen" ] && \ [ "$type" = "vnc-helper" -o "$type" = "windows-helper" ] && \ g="-geometry $(rematchfn '^([[:digit:]]+x[[:digit:]]+)' $screeninfo)" if [ "$r" = "-R" -a "$rootless" != "1" ]; then #nxlog "$lp Start nxproxy for single application session mode" #debug [ "$SET_LD_LIBRARY_PATH" = "1" ] && \ export LD_LIBRARY_PATH="$PROXY_LIBRARY_PATH:$LD_LIBRARY_PATH" nxlog "$lp Start nxproxy by command: '$PATH_BIN/nxproxy -C :$display $PROXY_EXTRA_OPTIONS'" $PATH_BIN/nxproxy -C :$display $PROXY_EXTRA_OPTIONS 2>&3 & else #nxlog "$lp nxagent session type (X11)" #debug [ "$SET_LD_LIBRARY_PATH" = "1" ] && \ export LD_LIBRARY_PATH="$AGENT_LIBRARY_PATH:$LD_LIBRARY_PATH" # Setup optional parameters p=$(node_agent_persistent_session) fp=""; [ -n "$AGENT_FONT_SERVER" ] && fp="-fp $AGENT_FONT_SERVER" if [ "$type" = "shadow" ]; then nxlog "$lp Type \"shadow\". Add some args to nxagent" local shmode=$ENABLE_INTERACTIVE_SESSION_SHADOWING [ "$shmode" = "1" ] && [ "$shadowviewonly" = "1" ] && shmode="0" r="-S -shadow $shadowhost:$shadowdisplay -shadowmode $shmode" p="-nopersistent" fi # Start the agent #nxlog "$lp env start `env`"; nxlog "$lp env end" nxlog "$lp Start nxagent by command: '$COMMAND_NXAGENT $p $r -name \"NX - $user@$SERVER_NAME:$display - $session (GPL Edition)\" -option \"$NXSESSION_DIRECTORY/options\" $b $fp $AGENT_EXTRA_OPTIONS_X :$display'" #PATH="$PATH_BIN:$PATH" $COMMAND_NXAGENT $p $r -name "NX - $user@$SERVER_NAME:$display - $session (GPL Edition)" -option "$NXSESSION_DIRECTORY/options" $k $g $b $fp $AGENT_EXTRA_OPTIONS_X :$display 2>&3 & PATH="$PATH_BIN:$PATH" $COMMAND_NXAGENT $p $r \ -name "NX - $user@$SERVER_NAME:$display - $session (GPL Edition)" \ -option "$NXSESSION_DIRECTORY/options" $b $fp \ $AGENT_EXTRA_OPTIONS_X :$display 2>&3 & fi # Wait for the agent agent_pid=$! usess_set "$session_id" "agent_pid" "$agent_pid" echo "NX> 733 Agent pid: $agent_pid:" nxlog "$lp Waiting for agent_pid='$agent_pid'" wait $agent_pid; agent_exit_status=$? nxlog "$lp agent_exit_status='$agent_exit_status'" node_failed="" if [ $agent_exit_status -ne 0 ]; then echo "NX> 1004 Error: NX Agent exited with exit status $agent_exit_status. To troubleshoot set SESSION_LOG_CLEAN=0 in node.conf and investigate \"$nx_dir/F-C-$sess_id/session\". You might also want to try: ssh -X myserver; $PATH_BIN/nxnode --agent to test the basic functionality. Session log follows:" echo "$(< $NXSESSION_DIRECTORY/session)" >&2 node_failed="Failed" nxlog "$lp node_failed='$node_failed'" fi #nxlog "$lp close session" #debug echo "NX> 1006 Session status: closed" # Cleanup session information #nxlog "$lp cleanup session information '$sess_id'" #debug #nxlog "$lp call 'node_terminate_session \"$session_id\" \"$node_failed\"'" #debug node_terminate_session "$session_id" "$node_failed" # remove possible leftovers of nxagent #nxlog "$FUNCNAME ($$):remove /tmp/.X$display-lock" #debug rm -f /tmp/.X$display-lock #nxlog "$lp remove /tmp/.X11-unix/X$display" #debug rm -f /tmp/.X11-unix/X$display } 3>&1 1>&4 | tee "$NXSESSION_DIRECTORY/session" | \ node_start_monitor; } 4>&1 } node_emergency_exit() { local lp="$FUNCNAME ($$/$BASHPID):"; #nxlog "$lp starting" #debug #nxlog "$lp call 'node_terminate_session \"$session_id\" \"Failed\"'" #debug node_terminate_session "$session_id" "Failed" echo "NX> 1004 Error: Emergency exit due to kill signal." #nxlog "$lp end" #debug } node_start_monitor() { #arg: # Monitoring the nxagent: Its also kind of a "state-machine" # as it has to keep track of different # connection states and react differently. local lp="$FUNCNAME ($$/$BASHPID):"; #nxlog "$lp starting with arg: $@" #debug local tail_pid="" watchdog_pid tosend pars_sent="" local smbport="0" mmport="0" cupsport="0" while read line; do case "$line" in *"Info: Waiting for connection from"*) [ -n "$pars_sent" ] && continue; # send params only once tosend="NX> 700 Session id: $sess_id NX> 705 Session display: $display\nNX> 703 Session type: $type NX> 701 Proxy cookie: $cookie\nNX> 702 Proxy IP: $proxyip NX> 706 Agent cookie: $cookie\nNX> 704 Session cache: $type NX> 707 SSL tunneling: $encryption\n" # File-sharing port options [ "$samba" = "1" ] && tosend+="NX> 709 File-sharing port: 445\n" echo -e "$tosend""NX> 710 Session status: running NX> 1002 Commit\nNX> 1006 Session status: running" pars_sent="1"; continue; ;; *"Info: Listening"*"SMB connections on port"*) # Catch NXAGENT SMB Port (sometimes the port differs from what we got from nxserver) smbport=$(cutfn "$line" 1 "'"); smbport=${smbport##*:} usess_set "$session_id" "smbport" "$smbport" continue; ;; *"Info: Listening"*"multimedia connections on port"*) # Catch NXAGENT Multimedia Port mmport=$(cutfn "$line" 1 "'"); mmport=${mmport##*:} usess_set "$session_id" "mmport" "$mmport" continue; ;; *"Info: Listening"*"CUPS connections on port"*) # Catch NXAGENT CUPS Port cupsport=$(cutfn "$line" 1 "'"); cupsport=${cupsport##*:} usess_set "$session_id" "cupsport" "$cupsport" continue; ;; *"Info: Watchdog running with pid"*) # Watchdog termination watchdog_pid=$(cutfn "$line" 1 "'") continue; ;; *"Info: Waiting the watchdog process to complete"*) # Kill the watchdog kill $watchdog_pid 2>/dev/null continue; ;; esac if [ "$1" != "restore" ]; then # "start" instance case "$line" in *"Session: Starting session at"*) echo "NX> 1009 Session status: starting" usess_set "$session_id" "status" "Starting" ;; *"Session: Session started at"*) usess_set "$session_id" "status" "Running" ;; *"Session: Suspending session at"*) echo "NX> 1009 Session status: suspending" usess_set "$session_id" "status" "Suspending" ;; *"Session: Terminating session at"*) echo "NX> 1009 Session status: terminating" ;; *"Session: Session suspended at"*) # Session suspend echo "NX> 1005 Session status: suspended" #nxlog "$lp $line" #debug ((mmport+smbport+cupsport>0)) && { #nxlog "$lp call node_stop_services" #debug node_stop_services } usess_set "$session_id" "status,userip,acc_ip" "Suspended&&" ;; esac else # "restore" instance #nxlog "$lp nxagent output: $line" case "$line" in *"Info: tail -f running with pid"*) # Catch tail pid tail_pid=$(cutfn "$line" 1 "'") usess_set "$session_id" "tail_pid" "$tail_pid" #echo "$node_tail_pid" >"$nx_dir/C-$sess_id/pids/tail" ;; *"Session: Resuming session at"*) echo "NX> 1009 Session status: resuming" usess_set "$session_id" "status" "Resuming" ;; *"Session: Session resumed at"*) # Reconnection success! echo "NX> 718 Session restore succeded" usess_set "$session_id" "status,userip,acc_ip"\ "Running&$userip&$accept" kill $tail_pid 2>/dev/null; break ;; *"Session: Display failure detected at"*) # Reconnection failure echo "NX> 596 Error: Session $1 failed. Reason was: $line" kill $tail_pid 2>/dev/null; break ;; esac fi done trap "" EXIT [ "$1" != "restore" -a "$rootless" != "1" ] && { nxlog "$lp call node_stop_services at ending" node_stop_services; } # close all open file descriptors exec 0<&-; exec 1>&-; exec 2>&-; #nxlog "$lp end" #debug exit_proc 0 } startsession() { # Start a new session. # 1.5.0 options: rdpcolors,rdpcache,http # nxclient > 1.5.0-106 variables: resize,keybd # FreeNX specific variables: clientproto,status,host # NX 3.0 shadow mode related variables: shadowusername,shadowcookie, # shadowdisplay,shadowhost # dimbor: additional extra-channels extra[1-3], patched nxcomp both # on server and client are required local lp="$FUNCNAME ($$/$BASHPID):"; local opt_vars opt_str pack cleanup product clipboard menu; local id fullscreen accept vn; local i ok step timeo #nxlog "$lp starting with args \"$@\"" #debug [ "$1" = "start" ] && mkdir -p -m700 "$NXSESSION_DIRECTORY" # Setup environment [ -n "$SOURCE_SYS_PROFILE" ] && . $SOURCE_SYS_PROFILE [ -n "$SOURCE_USER_PROFILE" ] && { if [ "${SOURCE_USER_PROFILE:0:1}" != "/" ]; then . $HOME/$SOURCE_USER_PROFILE else . $SOURCE_USER_PROFILE fi } [ "$PROXY_TCP_NODELAY" = "0" ] && nodelay=0 [ "$ENABLE_ROOTLESS_MODE" = "0" ] && rootless=0 [ -z "$samba" ] && samba=0; [ -z "$media" ] && media=0 [ -z "$shmem" ] && shmem=0; [ -z "$shpix" ] && shpix=0 [ "$geometry" = "fullscreen" ] && fullscreen="1" || fullscreen="0" [ -z "$nodelay" ] && nodelay=1 # ??? cleanup=10; product=LFE/None/LFEN/None; id=$sess_id; clipboard="$ENABLE_CLIPBOARD"; menu="$ENABLE_PULLDOWN_MENU" windows_app=$application [ -z "$keybd" ] && keybd=$aux # backwards compatibility for keybd parameter [ "$EXPORT_USERIP" = "1" ] && export NXUSERIP="$userip" [ "$EXPORT_SESSIONID" = "1" ] && export NXSESSIONID="$sess_id" export SHADOW_XAUTHORITY="$NXSESSION_DIRECTORY/authority" if [ "$type" = "vnc-helper" -o "$type" = "windows-helper" ]; then # We do not want to suspend such a session # as RDP/RFB are both suspendable as well ENABLE_PERSISTENT_SESSION="" fi if [ "$encryption" = "1" ]; then # we need to use the IP of the "calling" server now accept=$(rematchfn "($ip4_pattern)" "$SSH_CLIENT $SSH2_CLIENT") #" # If host is the same, use 127.0.0.1, else fallback to default [ -z "$accept" -a "$host" = "127.0.0.1" ] && accept="127.0.0.1" else encryption=0; accept=$userip fi # We need our own external IP proxyip="$EXTERNAL_PROXY_IP"; [ -z "$proxyip" ] && proxyip="127.0.0.1" pack="" if [ -z "$imagecompressionlevel" ]; then imagecompressionlevel="9" elif [ "$imagecompressionmethod" = "0" ]; then pack="nopack" elif [ "$imagecompressionmethod" = "1" ]; then pack="16m-jpeg-$imagecompressionlevel" elif [ "$imagecompressionmethod" = "2" ]; then pack="16m-png-9" fi if [ "$1" = "start" ]; then cookie=$(echo $[$RANDOM*$RANDOM] | $COMMAND_MD5SUM); cookie=${cookie%% *} # add row to usses with defaults: session_id, status, display, type, # client, agent_pid, cookie, tail_pid, userip, acc_ip, mmport, cupsport,\ # smbport usess_add "$sqcols_usess" "$session_id&Starting&$display&$type&$client&\ 0&$cookie&0&$userip&$accept&0&0&0&" elif [ "$1" = "restore" ]; then cookie=$(usess_get "$session_id" "cookie") fi if [ "$1" = "application" ]; then # This needs to be set, else nxagent is terminated rootless="1"; virtualdesktop="0" #nxlog "$lp call 'node_start_applications'" #debug node_start_applications & echo "NX> 596 Application $application started successfully." return fi #nxlog "$lp generate \"$NXSESSION_DIRECTORY/options\"" #debug opt_vars="keyboard kbtype kbload keymap geometry\ client resize cache images pack link nodelay type clipboard composite\ cleanup product shmem backingstore shpix accept cookie id samba media\ sync cups keybd aux http extra1 extra2 extra3 rdpcolors rdpcache\ fullscreen menu" opt_str="nx/nx"; for vn in $opt_vars; do [ -n "${!vn}" ] && opt_str+=",$vn=${!vn}"; done #[ "$type" = "shadow" ] && opt_str+=",shadow=1" opt_str+=":$display" # write options file umask 0077; echo "$opt_str" > "$NXSESSION_DIRECTORY/options"; umask $umask0 if [ "$1" = "start" ]; then # write xauth script file #nxlog "$lp write xauth script file" #debug txt="add localhost:$display MIT-MAGIC-COOKIE-1 $cookie add :$display MIT-MAGIC-COOKIE-1 $cookie exit" echo "$txt" | $COMMAND_XAUTH >/dev/null 2>&1 echo "$txt" | $COMMAND_XAUTH -f "$NXSESSION_DIRECTORY/authority" >/dev/null 2>&1 fi if [ -n "$shadowcookie" ]; then #nxlog "$lp If we have a shadow cookie, we add it to xauth session authority file as well" #debug $COMMAND_XAUTH -f "$SHADOW_XAUTHORITY" add "$shadowhost:$shadowdisplay" MIT-MAGIC-COOKIE-1 "$shadowcookie" elif [ -n "$shadowdisplay" ]; then # we need to merge in the normal .Xauthority file #nxlog "$lp we need to merge in the normal .Xauthority file" #debug $COMMAND_XAUTH -f "$SHADOW_XAUTHORITY" merge "$HOME/.Xauthority" fi if [ "$1" = "restore" ]; then #nxlog "$lp restore session" #debug #echo > "$NXSESSION_DIRECTORY/session" #this cause to damage file sh -c 'echo "Info: tail -f running with pid '\'\$$\''."; exec tail -n1 -f '"$NXSESSION_DIRECTORY"'/session' | node_start_monitor "restore" & MONITOR_PID=$!; export MONITOR_PID #nxlog "$lp call 'node_suspend_session \"$session_id\"'" #debug node_suspend_session "$session_id" || { echo "Info: Reconnection failed: NX Agent process could not be found." >> \ "$NXSESSION_DIRECTORY/session"; node_fail_restore_session "$session_id"; return 1; } else # start #nxlog "$lp call 'node_start_agent'" #debug node_start_agent & #nxlog "$lp call 'node_start_applications'" #debug node_start_applications & if [ -x "$NODE_AUTOSTART" ]; then #nxlog "$lp NODE_AUTOSTART: waiting for nxagent" #debug ok="" step="0.01" timeo=$((AGENT_STARTUP_TIMEOUT*100)) for (( i=0; i<=timeo; i++ )); do [ -f /tmp/.X$display-lock ] && { ok="1"; break; } sleep $step"s" done [ -n "$ok" ] || { nxlog "$lp /tmp/.X$display-lock not found after $AGENT_STARTUP_TIMEOUT s"; } # go into background immediately NXSESSIONID="$sess_id" DISPLAY=:$display "$NODE_AUTOSTART" "$1" >/dev/null 2>&1 & disown $! # dont't wait for this child! fi fi if [ -n "$mediahelper" -a "$1" != "application" ]; then #nxlog "$lp display='$display', waiting for it's ready" #debug ok="" step="0.01" timeo=$((AGENT_STARTUP_TIMEOUT*100)) for (( i=0; i<=timeo; i++ )); do [ -f /tmp/.X$display-lock ] && { ok="1"; break; } sleep $step"s" done [ -n "$ok" ] || \ nxlog "$lp /tmp/.X$display-lock not found after $AGENT_STARTUP_TIMEOUT s"; [ -n "$ok" ] && { #nxlog "$lp env: $(env)" #debug uservice_start ${mediahelper%%-*} "" "media-pa" "" "" "" "$mediahelper" } fi if [ -n "$MONITOR_PID" ]; then wait "$MONITOR_PID" usess_set "$session_id" "tail_pid" "0" fi wait # for all children #nxlog "$lp end" #debug } cmd_node_terminate() { echo "$delim 716 Terminating session $session_id on user request." node_terminate_session "$session_id" } cmd_node_suspend() { echo "$delim 716 Suspending session $session_id on user request." node_suspend_session "$session_id" } # ----------------------------------------------------------------------------- # Startup of nxnode # ----------------------------------------------------------------------------- declare -g delim="NX>" CMDLINE="" nx_dir nxuser_logfile umask0=$(umask); open_dbe $$ attach_db "$sq_settings_fn" ro || { echo "$delim 500 Error: NXNODE: Unable to attach db file $sq_settings_fn"; exit_proc 1; } set_vars_from_db "" $USER [ -n "$2" ] && delim="NX-$2>" echo "$delim 1000 NXNODE - Version $NX_VERSION $NX_LICENSE" if [ "$USER" = "nx" ]; then nx_dir="/var/lib/nxserver/home" # ??? nxuser_logfile="/var/log/nx/nxnode.log" else nx_dir="$HOME/.nx" [ -d $nx_dir ] || { umask 0077; mkdir -p $nx_dir; umask $umask0; } nxuser_logfile="$nx_dir/nxnode.log" fi attach_db "$nx_dir/usessions.sq3" && { init_usessions_db; chmod 0600 "$nx_dir/usessions.sq3" >/dev/null 2>&1; } if ! stringinstring "$1" "--check|--setkey|--agent"; then read CMDLINE; set_vars_from_ampstr "$CMDLINE" "" "recode" if [ -z "$session_id" ]; then echo "NX> 500 Error: Fatal - Missing parameter session id." 1>&2 exit_proc 1 fi declare -g sess_id="$SERVER_NAME-$display-$session_id" declare -g NXSESSION_DIRECTORY="$nx_dir/C-$sess_id" nxuser_logfile="$nx_dir/nxnode-$session_id.log" nxlog "$0 ($$): run nxnode with PARAMS:\"$@\"; CMDLINE='$CMDLINE'" else nxlog "$0 ($$): run nxnode with \"$@\"" fi case "$1" in --startsession) startsession "start" ;; --resumesession) startsession "restore" ;; --applicationsession) startsession "application" ;; --terminate) cmd_node_terminate ;; --suspend) cmd_node_suspend ;; --smbmount) [ "$(usess_get $session_id "client")" = "winnt" ] && { username=$(getparam "$CMDLINE" "username") # reread with no hex recode computername=$(getparam "$CMDLINE" "computername") username=$(cp_conv "$username"); password=$(cp_conv "$password") share=$(cp_conv "$share"); dir=$(cp_conv "$dir") computername=$(cp_conv "$computername") } uservice_start "//127.0.0.1/$share" "" "smb-share" "$share" \ "$username" "$password" "$dir" "$computername" ;; --addprinter) [ "$(usess_get $session_id "client")" = "winnt" ] && { username=$(getparam "$CMDLINE" "username") # reread with no hex recode computername=$(getparam "$CMDLINE" "computername") username=$(cp_conv "$username"); password=$(cp_conv "$password") share=$(cp_conv "$share"); computername=$(cp_conv "$computername") } [ -n "$defaultPrinter" ] && defaultprinter=$defaultPrinter opts="model=$model;public=$public;defaultprinter=$defaultprinter" [ "$type" = "ipp" ] && share=$printer; svc=$share [ "${svc:0:1}" = "@" ] && svc=${svc:1} # for backward compatibility svc=${svc%-nocheck}; svc="$USER""_${svc%%__*}" uservice_start "$svc" "" "$type-prn" "$share"\ "$username" "$password" "$opts" "$computername" ;; --check) echo "NX> 716 finished" ;; --agent) echo "NX> 716 Starting NX Agent ..." shift [ "$SET_LD_LIBRARY_PATH" = "1" ] && export LD_LIBRARY_PATH="$AGENT_LIBRARY_PATH:$LD_LIBRARY_PATH" PATH="$PATH:$PATH_BIN" $COMMAND_NXAGENT \ -name "NX Agent Test - Args: $@" $@ echo "NX> 716 NX Agent exited with status: $?" ;; --setkey) mkdir -m 700 -p $HOME/.ssh if ! grep -q "$(cat $NX_ETC_DIR/users.id_dsa.pub)" $HOME/.ssh/$SSH_AUTHORIZED_KEYS 2>/dev/null; then cat $NX_ETC_DIR/users.id_dsa.pub >> $HOME/.ssh/$SSH_AUTHORIZED_KEYS chmod 600 $HOME/.ssh/$SSH_AUTHORIZED_KEYS echo "NX> 716 Public key added to: $HOME/.ssh/$SSH_AUTHORIZED_KEYS" else echo "NX> 716 Public key is already present in: $HOME/.ssh/$SSH_AUTHORIZED_KEYS" fi ;; *) echo "NX> 500 Error: Command not found" ;; esac echo "$delim 1001 Bye." exit_proc 0