########################################################################### # Implements the client side of the epoptes communications protocol. # The daemon reads this file when it starts, and sends it to clients when they # connect. The clients actually source it and then wait for further commands. # # Copyright (C) 2010, 2012 Alkis Georgopoulos # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # On Debian GNU/Linux systems, the complete text of the GNU General # Public License can be found in `/usr/share/common-licenses/GPL'. ########################################################################### # Output a message and exit with an error. # Parameters: # $1..$N = The message. die() { echo "epoptes-client ERROR: $@" >&2 exit 1 } # Calculate, export and return a collection of useful variables. info() { local server_ip def_iface if [ -z "$cached_info" ]; then VERSION=${VERSION:-0.4.3} # Just in case the client wasn't updated test -n "$USER" || USER=$(whoami) NAME=$(getent passwd "$UID" | cut -d':' -f5 | cut -d',' -f1) test -n "$HOME" || HOME=$(getent passwd "$UID" | cut -d: -f6) if [ -n "$LTSP_CLIENT_HOSTNAME" ]; then HOSTNAME="$LTSP_CLIENT_HOSTNAME" else HOSTNAME=$(hostname) test -n "$HOSTNAME" || die "Empty hostname" fi if [ -n "$LTSP_CLIENT" ] && [ -n "$LTSP_CLIENT_MAC" ]; then # LTSP exports those vars, use them if available. MAC="$LTSP_CLIENT_MAC" IP="$LTSP_CLIENT" else server_ip=$(getent hosts "$SERVER" | cut -d' ' -f1) def_iface=$(ip -oneline -family inet route get "$server_ip" \ | sed -n '/.* dev \([^ ]*\).*/s//\1/p') test "${def_iface:-lo}" = "lo" && def_iface=$(ip -oneline -family \ inet route show | sed -n '/^default .* dev \([^ ]*\).*/s//\1/p') def_iface=${def_iface:-eth0} MAC=$(ip -oneline -family inet link show dev "$def_iface" \ | sed "s/.*ether \([^ ]*\).*/\\1/") MAC=$(echo "$MAC" | sed 'y/abcdef-/ABCDEF:/;s/[^A-F0-9:]//g') test -n "$MAC" || die "Empty MAC" IP=$(ip -oneline -family inet addr show dev "$def_iface" \ | sed "s/.* \([0-9.]*\)\/.*/\\1/") test -n "$IP" || die "Empty IP" fi CPU=$(cat /proc/cpuinfo | grep "^model name" | head -n 1 | sed "s/.*: //") RAM=$(free -m | grep "^Mem" | awk '{print $2}') VGA=$(lspci -nn -m | sed -n -e '/"VGA/s/[^"]* "[^"]*" "[^"]*" "\([^"]*\)" .*/\1/p' | tr '\n' ' ') OS=$(uname -o) export VERSION USER NAME HOME HOSTNAME MAC IP CPU RAM VGA OS cached_info=true fi cat </dev/null ) & test -n "$print_pid" && echo $! } # Execute a command in the background. # Parameters: # $1 = the command. execute() { local launcher # Do some logging, either in ~/.xsession-errors or on the console. echo "$(LANG=C date '+%c'), epoptes-client executing: $1" >&2 case "$1" in '') echo "Can't execute an empty command." >&2 ;; www.*) set "http://$1" launcher="xdg-open" ;; http:*|https:*|ftp:*|file:*|mailto:*) launcher="xdg-open" ;; *) if [ -e "$1" ] && ( [ ! -x "$1" ] || [ -d "$1" ] ); then launcher="xdg-open" elif which -- "$1" >/dev/null; then # Don't waste RAM for sh if it's just an executable. launcher="" fi esac # In all unhandled cases, run the command with sh -c. launcher=${launcher-sh -c} background $launcher "$1" } # Log out the connected user. logout() { ./endsession --logout } # Reboot the client. reboot() { ./endsession --reboot } # Shut down the client. shutdown() { ./endsession --shutdown } # Create a thumbnail of the user screen. # Parameters: # $1 = thumbnail width. # $2 = thumbnail height. screenshot() { if ./screenshot "$1" "$2"; then BAD_SCREENSHOTS=0 elif [ "$BAD_SCREENSHOTS" -eq 3 ]; then die "3 failed screenshots, exiting..." else BAD_SCREENSHOTS=$(($BAD_SCREENSHOTS+1)) fi } # Lock the screen. # Parameters: # $1 = seconds to keep screen locked, 0 means forever - currently ignored. # $2 = message to display to the user. lock_screen() { test -n "$EPOPTES_LOCK_SCREEN_PID" && kill "$EPOPTES_LOCK_SCREEN_PID" EPOPTES_LOCK_SCREEN_PID=$(background -p ./lock-screen "$2") } # Unlock a locked screen. unlock_screen() { if [ -n "$EPOPTES_LOCK_SCREEN_PID" ]; then kill "$EPOPTES_LOCK_SCREEN_PID" unset EPOPTES_LOCK_SCREEN_PID fi } # Mute the system sound. # Parameters: # $1 = seconds to keep sound muted, 0 means forever - currently ignored. mute_sound() { if [ -x /usr/bin/pactl ]; then background pactl set-sink-mute @DEFAULT_SINK@ 1 elif [ -x /usr/bin/amixer ]; then background amixer -c 0 -q sset Master mute fi } # Unute the system sound. unmute_sound() { if [ -x /usr/bin/pactl ]; then background pactl set-sink-mute @DEFAULT_SINK@ 0 elif [ -x /usr/bin/amixer ]; then background amixer -c 0 -q sset Master unmute fi } # Display some text to the user. # Parameters: # $1 = text. # $2 = title. # $3 = True/False, whether the text contains pango markup. message() { background ./message "$1" "$2" "$3" } # Connect to the server to be monitored. # Parameters: # $1 = port. get_monitored() { background x11vnc -noshm -24to32 -viewonly -connect_or_exit "$1" } # Connect to the server to get assistance. # Parameters: # $1 = port. # $2 = grab keyboard and mouse. get_assisted() { background x11vnc -noshm -24to32 ${2:+-grabptr -grabkbd} -connect_or_exit "$1" } # Deactivate the screensaver, in order for the users to watch a broadcast. stop_screensaver() { if [ -x /usr/bin/gnome-screensaver-command ]; then gnome-screensaver-command -d fi } # Receive a broadcasted screen from the server. # Parameters: # $1 = port. # $2 = password (encrypted as in ~/.vnc/passwd and octal-escaped). # $3 = fullscreen. receive_broadcast() { stop_receptions test "$UID" -eq 0 && export $(./get-display) xset dpms force on if [ -z "$VNCVIEWER" ]; then # If the user installed ssvnc, prefer it over xvnc4viewer if [ -x /usr/bin/ssvncviewer ]; then VNCVIEWER=ssvncviewer elif [ -x /usr/bin/xvnc4viewer ]; then VNCVIEWER=xvnc4viewer fi fi printf "$2" | { sleep 0.$(($(hexdump -e \"%d\" -n 2 /dev/urandom) % 50 + 50)) if [ "$VNCVIEWER" = "ssvncviewer" ]; then exec ssvncviewer -shared -viewonly -passwd /dev/stdin \ ${3:+-fullscreen} "$1" elif [ "$VNCVIEWER" = "xvnc4viewer" ]; then exec xvnc4viewer -Shared -ViewOnly -passwd /dev/stdin \ ${3:+-FullScreen -UseLocalCursor=0 -MenuKey F13} "$1" else exec vncviewer -shared -viewonly -passwd /dev/stdin \ ${3:+-fullscreen} "$1" fi } >/dev/null 2>&1 & EPOPTES_VNCVIEWER_PID="$!" } # The vnc clients should automatically exit when the server is killed. # Unfortunately, that isn't always true, so try to kill them anyway. stop_receptions() { if [ -n "$EPOPTES_VNCVIEWER_PID" ]; then kill "$EPOPTES_VNCVIEWER_PID" unset EPOPTES_VNCVIEWER_PID fi } # Open a root terminal inside the X session. root_term() { background xterm -e bash -l } # Send a screen session to the server using socat. # Parameters: # $1 = port. remote_term() { local screen_params REM=$1 if [ "$UID" -eq 0 ]; then REM="$SERVER:${1#*:}" screen_params="bash -l" else screen_params="-l" fi background sh -c " cd sleep 1 TERM=xterm exec socat EXEC:'screen $screen_params',pty,stderr tcp:$REM" } # Ping is called every few seconds just to make sure the connection is alive. # But currently we use it as a workaround to kill stale clients too: # Epoptes-client isn't registered as an X session client, and it doesn't # exit automatically, so tell it to exit as soon as X is unavailable. ping() { if [ "$UID" -gt 0 ]; then xprop -root -f EPOPTES_CLIENT 32c -set EPOPTES_CLIENT $$ || exit fi } # Display a message. # Parameters: # $1..$N = The message. # echo() # No need to implement it in the shell, it's embedded. # Main if [ -z "$UID" ] || [ -z "$TYPE" ] || [ -z "$SERVER" ]; then die "Required environment variables are missing." fi # Source the lsb init functions, for log_end_msg. # Unfortunately it seems that Centos and Fedora don't have that file. if [ -f /lib/lsb/init-functions ]; then ( # Use a subshell, we only need init-functions once . /lib/lsb/init-functions log_end_msg 0 >&5 ) else echo "[ OK ]" >&5 fi info