From ec450fcfa87756a54f5a3e5654c9814bae83a49f Mon Sep 17 00:00:00 2001 From: Alkis Georgopoulos Date: Mon, 27 Feb 2012 21:53:56 +0200 Subject: [PATCH] Extract shell source from gui.py for maintainability. --- data/client-functions | 251 ++++++++++++++++++++++++++++++++++ epoptes-client.8 | 10 +- epoptes-client/epoptes-client | 163 +++++++++------------- epoptes/__init__.py | 2 +- epoptes/common/commands.py | 60 -------- epoptes/daemon/bashplex.py | 2 +- epoptes/ui/gui.py | 157 +++++++-------------- setup.py | 2 +- 8 files changed, 376 insertions(+), 271 deletions(-) create mode 100644 data/client-functions delete mode 100644 epoptes/common/commands.py diff --git a/data/client-functions b/data/client-functions new file mode 100644 index 0000000..51e62b9 --- /dev/null +++ b/data/client-functions @@ -0,0 +1,251 @@ +########################################################################### +# 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 def_iface + + if [ -z cached_info ]; then + # TODO: use `ip route get server-ip` instead + def_iface=$(ip -oneline -family inet route show \ + | sed -n '/^default .* dev \([^ ]*\).*/s//\1/p') + test -z "$def_iface" && die "Empty def_iface" + + test -n "$USER" || USER=$(whoami) + test -n "$HOME" || HOME=$(getent passwd "$UID" | cut -d: -f6) + HOSTNAME=$(hostname) + test -n "$HOSTNAME" || die "Empty hostname" + IP=$(ip -oneline -family inet addr show dev "$def_iface" | sed "s/.* \([0-9.]*\)\/.*/\\1/") + test -n "$IP" || die "Empty IP" + 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" + CPU=$(cat /proc/cpuinfo | grep "^model name" | head -1 | sed "s/.*: //") + RAM=$(free -m | grep "^Mem" | awk '{print $2}') + VGA=$(lspci -nn -m | sed -n -e '/"VGA/s/[^"]* "[^"]*" "[^"]*" "\([^"]*\)" .*/\1/p') + OS=$(uname -o) + + # If epoptes-client is ran on a thin client from a user account (meaning + # that it actually runs on the server), then use $LTSP_CLIENT_HOSTNAME, + # $LTSP_CLIENT and $LTSP_CLIENT_MAC instead of $HOSTNAME, $IP and $MAC. + # CPU, RAM and VGA are not available in the environment, so we're leaving + # the ones of the server. + if [ "$TYPE" = "thin" ] && [ "$UID" -ne 0 ]; then + test -n "$LTSP_CLIENT" && IP="$LTSP_CLIENT" + test -n "$LTSP_CLIENT_HOSTNAME" && HOSTNAME="$LTSP_CLIENT_HOSTNAME" + test -n "$LTSP_CLIENT_MAC" && MAC="$LTSP_CLIENT_MAC" + fi + + export HOSTNAME IP MAC TYPE USER UID CPU RAM VGA SERVER PORT + cached_info=true + fi + cat </dev/null || set "xdg-open" "$1" + fi + + # Do some logging, either in ~/.xsession-errors or on the console. + echo "$(LANG=C date '+%c'), epoptes-client executing: $@" >&2 + + # The command is ran with stdin and stdout redirected to /dev/null, + # so that it doesn't interfere with the output of other commands. + # stderr isn't changed, i.e. ~/.xsession-errors will be used. + "$@" 0/dev/null & + + # Print the pid. + echo $! +} + +# 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 %i %i; 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=$(execute ./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() { + execute amixer -c 0 -q sset Master mute +} + +# Unute the system sound. +unmute_sound() { + execute amixer -c 0 -q sset Master unmute +} + +# Display some text to the user. +# Parameters: +# $1 = text. +# $2 = dialog type, one of "info", "warning" or "error". +message() { + local type + + type=${2:-info} + if [ -x /usr/bin/zenity ]; then + execute zenity "--$type" --text "$1" + elif [ -x /usr/bin/xmessage ]; then + execute xmessage -center "$1" + else + echo "$type: $1" >&2 + fi +} + +# Connect to the server to be monitored. +get_monitored() { + execute x11vnc -noshm -24to32 -viewonly -connect_or_exit "$SERVER" +} + +# Connect to the server to get assistance. +get_assisted() { + execute x11vnc -noshm -24to32 -connect_or_exit "$SERVER" +} + +# 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 = fullscreen. +receive_broadcast() { + stop_transmissions + export $(./get-display) + xset dpms force on + EPOPTES_VNCVIEWER_PID=$(execute sh -c " +sleep 0.$(($(hexdump -e \"%d\" -n 2 /dev/urandom) % 50 + 50)) +exec xvnc4viewer -Shared -ViewOnly ${2+-FullScreen -UseLocalCursor=0 -MenuKey F13} $SERVER:$1") +} + +# The vnc clients should automatically exit when the server is killed. +# Unfortunately, that isn't always true, so try to kill them anyway. +stop_transmissions() { + test -n "$EPOPTES_VNCVIEWER_PID" && kill "$EPOPTES_VNCVIEWER_PID" + unset EPOPTES_VNCVIEWER_PID +} + +# Open a root terminal inside the X session. +root_term() { + export $(./get-display) + execute xterm -e bash -l +} + +# Sends a screen session to the server using socat. +# Parameters: +# $1 = port. +remote_term() { + local screen_params + + if [ "$UID" -eq 0 ]; then + screen_params="bash -l" + else + screen_params="-l" + fi + execute sh -c " +cd +sleep 1 +TERM=xterm exec socat EXEC:'screen $screen_params',pty,stderr tcp:$SERVER:$1" +} + +# Main + +# Source the lsb init functions, for log_begin_msg / log_end_msg. +# Unfortunately it seems that Centos and Fedora don't have that file. +if [ -f /lib/lsb/init-functions ]; then + . /lib/lsb/init-functions +else + alias log_begin_msg=echo + alias log_warning_msg=echo + alias log_end_msg=echo +fi +log_end_msg 0 >&5 diff --git a/epoptes-client.8 b/epoptes-client.8 index f3181b4..9ec688e 100644 --- a/epoptes-client.8 +++ b/epoptes-client.8 @@ -1,11 +1,11 @@ -.TH EPOPTES-CLIENT 8 "2011-11-03" epoptes-client epoptes-client +.TH EPOPTES-CLIENT 8 "2012-02-27" epoptes-client epoptes-client .SH "NAME" epoptes\-client \- Client side daemon for epoptes .SH "SYNOPSIS" .IX Header "SYNOPSIS" -epoptes\-client [\-c] [SERVER] [PORT] +epoptes\-client [OPTION] [SERVER] [PORT] .SH "DESCRIPTION" .IX Header "DESCRIPTION" @@ -14,13 +14,17 @@ epoptes\-client [\-c] [SERVER] [PORT] .SH "OPTIONS" .IP "\fB\-c\fP" 10 Fetch the /etc/epoptes/server.crt certificate from the server. +.IP "\fB\-h\fP" 10 +Print a help message. +.IP "\fB\-v\fP" 10 +Display version information. .SH "SEE ALSO" \fBepoptes\fP\fB(1).\fP .SH "AUTHOR" .IX Header "AUTHOR" -Fotis Tsamis , Alkis Georgopoulos . +Alkis Georgopoulos , Fotis Tsamis . .SH "LICENSE" .IX Header "LICENSE" diff --git a/epoptes-client/epoptes-client b/epoptes-client/epoptes-client index ff9f841..ae75331 100755 --- a/epoptes-client/epoptes-client +++ b/epoptes-client/epoptes-client @@ -1,10 +1,10 @@ -#!/bin/bash +#!/bin/sh ########################################################################### # Connects to a remote server and offers it a local shell. # Usage: epoptes [server] [port] # -# Copyright (C) 2010, 2011 Alkis Georgopoulos +# 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 @@ -56,11 +56,9 @@ chrooted() { return 0 } -# Get $USER, $UID and $TYPE of the client, and the default $SERVER and $PORT. +# Get $UID and $TYPE of the client, and the default $SERVER and $PORT. basic_info() { - test -n "$USER" || export USER=$(whoami) - test -n "$UID" || export UID=$(id -u) - test -n "$HOME" || export HOME=$(getent passwd "$UID" | cut -d: -f6) + test -n "$UID" || UID=$(id -u) # TODO: fix upstream "$LTSP_FATCLIENT" to be present in user sessions if my_boolean_is_true "$LTSP_FATCLIENT" && [ -f /etc/ltsp_fat_chroot ]; then @@ -79,50 +77,8 @@ basic_info() { SERVER=server fi PORT=789 -} - -epoptes_info() { - local def_iface - - # TODO: use `ip route get server-ip` instead - while true; do - def_iface=$(ip -oneline -family inet route show \ - | sed -n '/^default .* dev \([^ ]*\).*/s//\1/p') -# test -n "$def_iface" || def_iface=$(ip -oneline -family inet link show \ -# | sed -n '/[0-9]*: \([^:]*\).*link\/ether.*/{s//\1/p;q}') - # If we got it, continue. - test -n "$def_iface" && break - # On dual NIC standalone workstartions, if the "non-default-route" - # interface is brought up first, exit, we'll be called again later. - test $UID -eq 0 && die "Empty def_iface, probably 2-NIC workstation, we'll get called again for the second NIC.." - # At this point, it's probably a user epoptes-client that doesn't have - # a network connection up yet. Retry until it's ready. - sleep 10 - done - - HOSTNAME=$(hostname) - test -n "$HOSTNAME" || die "Empty hostname" - IP=$(ip -oneline -family inet addr show dev "$def_iface" | sed "s/.* \([0-9.]*\)\/.*/\\1/") - test -n "$IP" || die "Empty IP" - 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" - CPU=$(cat /proc/cpuinfo | grep "^model name" | head -1 | sed "s/.*: //") - RAM=$(free -m | grep "^Mem" | awk '{print $2}') - VGA=$(lspci -nn -m | sed -n -e '/"VGA/s/[^"]* "[^"]*" "[^"]*" "\([^"]*\)" .*/\1/p') - - # If epoptes-client is ran on a thin client from a user account (meaning - # that it actually runs on the server), then use $LTSP_CLIENT_HOSTNAME, - # $LTSP_CLIENT and $LTSP_CLIENT_MAC instead of $HOSTNAME, $IP and $MAC. - # CPU, RAM and VGA are not available in the environment, so we're leaving - # the ones of the server. - if [ "$TYPE" = "thin" ] && [ "$UID" -ne 0 ]; then - test -n "$LTSP_CLIENT" && IP="$LTSP_CLIENT" - test -n "$LTSP_CLIENT_HOSTNAME" && HOSTNAME="$LTSP_CLIENT_HOSTNAME" - test -n "$LTSP_CLIENT_MAC" && MAC="$LTSP_CLIENT_MAC" - fi - export HOSTNAME IP MAC TYPE USER UID CPU RAM VGA SERVER PORT + export UID TYPE SERVER PORT } fetch_certificate() @@ -142,6 +98,27 @@ fetch_certificate() # Main. +export VERSION="0.4.3" # Automatically updated by mkdst + +# Check the first parameter as it may turn out we don't need to run at all +case "$1" in + -v|--version) + echo "$VERSION" + exit + ;; + -h|--help) + if [ -x /usr/bin/man ]; then + exec man epoptes-client + else + echo "Usage: $0 [-c|-h|-v] [SERVER] [PORT]" + exit 0 + fi + ;; + -c|--certificate) + need_certificate=true + shift + ;; +esac # When called from /etc/xdg/autostart, /sbin is not in the system path. PATH="$PATH:/sbin:/usr/sbin" @@ -157,12 +134,8 @@ if [ -f /etc/default/epoptes-client ]; then . /etc/default/epoptes-client fi # And the command line parameters override the configuration file -if [ "$1" = "-c" ]; then - need_certificate=true - shift -fi -SERVER=${1:-$SERVER} -PORT=${2:-$PORT} +export SERVER=${1:-$SERVER} +export PORT=${2:-$PORT} # Provide an easy way to fetch the server certificate test -n "$need_certificate" && fetch_certificate @@ -181,8 +154,6 @@ else cd /usr/share/epoptes-client fi -epoptes_info - # Source the lsb init functions, for log_begin_msg / log_end_msg. # Unfortunately it seems that Centos and Fedora don't have that file. if [ -f /lib/lsb/init-functions ]; then @@ -193,51 +164,41 @@ else alias log_end_msg=echo fi -# Epoptes-client is called two times and runs in three phases: -# In the first phase, some initialization is done, and socat is exec'ed. -# In the second phase, socat has successfully connected, and calls -# epoptes-client again to resume execution. Epoptes-client has to go through -# some initialization again, and it finally exec's a plain /bin/sh with -# the stdio descriptors that were inherited from socat. -# That's the third phase, a plain shell. But bash'es `exec -a` is used, so -# that "epoptes-client" is displayed in `ps`, instead of "/bin/sh". -if [ -z "$EPOPTES_CLIENT_PHASE" ]; then - export EPOPTES_CLIENT_PHASE=1 - - # Kill all ghost instances of epoptes-client of the same user. - # That may happen if network connectivity is lost for a while. - # Standalone workstations don't hang if the network is down, and nbd might cope - # with that for LTSP clients, but epoptes kills disconnected epoptes-clients. - # Exclude the current epoptes-client. - pkill -U $UID -f '^epoptes-client \+m' - - log_begin_msg "Epoptes-client connecting to $SERVER:$PORT..." - - # Remember the stdout descriptor to use it in the second phase. - # stdio will be redirected to the server, but stderr will be kept in the - # local console, to avoid possible noise from applications started in the - # background. - # If the callee needs to grab stderr, it can use `cmd 2>&1`. - exec 5>&1 - - # Connect to the server, or keep retrying until the server gets online - # (for standalone workstations booted before the server). - export EPOPTES_CLIENT_PHASE=2 - if [ -s /etc/epoptes/server.crt ]; then - exec socat openssl-connect:$SERVER:$PORT,cafile=/etc/epoptes/server.crt,interval=60,forever, EXEC:"$0 $*" - elif [ -f /etc/epoptes/server.crt ]; then - exec socat tcp:$SERVER:$PORT,interval=60,forever EXEC:"$0 $*",nofork - else - die " +# Call chain: +# * if-up.d executes /usr/sbin/epoptes-client +# * then socat is called +# * after a successful connection, socat exec's /bin/sh +# * and the daemon sends /usr/share/epoptes/client-functions to that shell + +# Kill all ghost instances of epoptes-client of the same user. +# That may happen if network connectivity is lost for a while. +# Standalone workstations don't hang if the network is down, and nbd might cope +# with that for LTSP clients, but epoptes kills disconnected epoptes-clients. +# The current epoptes-client is excluded because it starts with /bin/sh. +pkill -U $UID -f '^epoptes-client$' + +log_begin_msg "Epoptes-client connecting to $SERVER:$PORT..." + +# Remember the stdout descriptor to use it in the second phase. +# stdio will be redirected to the server, but stderr will be kept in the +# local console, to avoid possible noise from applications started in the +# background. +# If the callee needs to grab stderr, it can use `cmd 2>&1`. +exec 5>&1 + +# Bash supports launching a program with a different zeroth argument, +# this makes pgrep'ping for epoptes-client easier. +cmdline='bash -c "exec -a epoptes-client sh"' + +# Connect to the server, or keep retrying until the server gets online +# (for standalone workstations booted before the server). +if [ -s /etc/epoptes/server.crt ]; then + exec socat openssl-connect:$SERVER:$PORT,cafile=/etc/epoptes/server.crt,interval=60,forever, EXEC:"$cmdline" +elif [ -f /etc/epoptes/server.crt ]; then + exec socat tcp:$SERVER:$PORT,interval=60,forever EXEC:"$cmdline",nofork +else + die " The epoptes certificate file, /etc/epoptes/server.crt, doesn't exist. You can fetch the server certificate by running: $0 -c" - fi -elif [ "$EPOPTES_CLIENT_PHASE" = "2" ]; then - log_end_msg 0 >&5 - # Finally, exec sh instead of keeping bash, to save memory. - # But use "epoptes-client" as the zeroth argument, to make it easier for `ps`. - # +m = disable job control. - unset EPOPTES_CLIENT_PHASE - exec -a epoptes-client /bin/sh +m fi diff --git a/epoptes/__init__.py b/epoptes/__init__.py index 10d2ed0..63fa61b 100644 --- a/epoptes/__init__.py +++ b/epoptes/__init__.py @@ -9,6 +9,6 @@ You should have received a copy of the GNU General Public License along with Epoptes. If not, see .''' -__version__ = "0.4.3" +__version__ = "0.4.3" # Automatically updated by mkdst __license__ = "GPL" diff --git a/epoptes/common/commands.py b/epoptes/common/commands.py deleted file mode 100644 index a68ff88..0000000 --- a/epoptes/common/commands.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -########################################################################### -# Common commands. -# -# Copyright (C) 2010 Fotis Tsamis -# -# 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 FINESS 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". -########################################################################### - -import os - -class commands: - """ - Define here all epoptes custom commands - """ - - def __init__(self): - - self.POWEROFF = './endsession --shutdown ' - self.REBOOT = './endsession --reboot ' - self.LOGOUT = './endsession --logout ' - self.EXEC = './execute ' - - self.SCREENSHOT = 'if ./screenshot %i %i; \ - then BAD_SCREENSHOTS=0; elif [ "$BAD_SCREENSHOTS" = 3 ]; \ - then exit 1; else BAD_SCREENSHOTS=$(($BAD_SCREENSHOTS+1)); fi' - - self.EXEC_AMIXER = './execute amixer -c 0 -q sset Master ' - - self.POWEROFF_WARN = _('Are you sure you want to shutdown all the computers?') - self.REBOOT_WARN = _('Are you sure you want to reboot all the computers?') - self.LOGOUT_WARN = _('Are you sure you want to log off all the users?') - self.KILLALL_WARN = _('Are you sure you want to terminate all processes of the selected users?') - - def __setattr__(self, cmd, val): - """ - Set new constants and prevent from changing values from already - set constants - """ - - if hasattr(self, cmd): - raise ValueError, 'Command %s is already set' % cmd - - self.__dict__[cmd] = val diff --git a/epoptes/daemon/bashplex.py b/epoptes/daemon/bashplex.py index 0819697..97bdf7a 100644 --- a/epoptes/daemon/bashplex.py +++ b/epoptes/daemon/bashplex.py @@ -95,7 +95,7 @@ def connectionMade(self): self.pingTimer = reactor.callLater(self.factory.pingInterval, self.ping) - def connectionLost(self, reaspn): + def connectionLost(self, reason): try: self.pingTimeout.cancel() except Exception: pass diff --git a/epoptes/ui/gui.py b/epoptes/ui/gui.py index ea71ded..7bd2c8c 100644 --- a/epoptes/ui/gui.py +++ b/epoptes/ui/gui.py @@ -49,7 +49,6 @@ from remote_assistance import RemoteAssistance from epoptes.daemon import uiconnection from epoptes.core.lib_users import * -from epoptes.common import commands from epoptes.common import ltsconf from epoptes.common import config from epoptes.common.constants import * @@ -60,7 +59,6 @@ class EpoptesGui(object): def __init__(self): - self.c = commands.commands() self.shownCompatibilityWarning = False self.vncserver = None self.vncviewer = None @@ -173,19 +171,20 @@ def wake_on_lan(self, widget): def poweroff(self, widget): """Shut down the selected clients.""" - self.execOnSelectedClients(self.c.POWEROFF, root="auto", - warn=self.c.POWEROFF_WARN) + self.execOnSelectedClients("shutdown", root="auto", + warn=_('Are you sure you want to shutdown all the computers?')) def reboot(self, widget): """Reboot the selected clients.""" # FIXME: (Not) waiting on purpose to cause some delay to avoid # any power strain. - self.execOnSelectedClients(self.c.REBOOT, root="auto", - warn=self.c.REBOOT_WARN) + self.execOnSelectedClients("logout", root="auto", + warn=_('Are you sure you want to reboot all the computers?')) def logout(self, widget): """Log off the users of the selected clients.""" - self.execOnSelectedClients(self.c.LOGOUT, warn=self.c.LOGOUT_WARN) + self.execOnSelectedClients("logoff", + warn=_('Are you sure you want to log off all the users?') def reverseConnection(self, widget, path, view_column, cmd): @@ -194,17 +193,15 @@ def reverseConnection(self, widget, path, view_column, cmd): self.vncviewer = subprocess.Popen(['xvnc4viewer', '-listen']) # And, tell the clients to connect to the server - self.execOnSelectedClients(self.c.EXEC + cmd) + self.execOnSelectedClients(cmd) def assistUser(self, widget, path=None, view_column=None): - self.reverseConnection(widget, path, view_column, - 'x11vnc -noshm -24to32 -connect_or_exit $SERVER') + self.reverseConnection(widget, path, view_column, 'get_assisted') def monitorUser(self, widget, path=None, view_column=None): - self.reverseConnection(widget, path, view_column, - 'x11vnc -noshm -24to32 -viewonly -connect_or_exit $SERVER') + self.reverseConnection(widget, path, view_column, 'get_monitored') def findUnusedPort(self, base=None): @@ -230,7 +227,7 @@ def findUnusedPort(self, base=None): return None - def _broadcastScreen(self, fullscreen=True): + def _broadcastScreen(self, fullscreen=''): if self.vncserver is None: self.vncport = self.findUnusedPort() # TODO: use a password instead of -allow @@ -238,37 +235,18 @@ def _broadcastScreen(self, fullscreen=True): '-quiet', '-viewonly', '-shared', '-forever', '-nolookup', '-24to32', '-rfbport', str(self.vncport), '-allow', '127.,192.168.,10.,169.254.' ]) - self.execOnSelectedClients("""killall gnome-screensaver 2>/dev/null""") - # TODO: don't use sleep on the direct client shell, use execute script instead - # pgrep -x only checks the first 15 characters found in /proc/pid/stat. - # Check the length with e.g.: x="lxdm-greeter-gtk"; echo ${x:0:15} - # The following greeters spawn dbus-daemon, so there's no need for them - # to be in the greeters list: - # gdm-simple-greeter, unity-greeter - # Unfortunately, dbus-daemon doesn't contain DBUS_SESSION_BUS_ADDRESS. - - fullscreen_args = '' - if fullscreen: - fullscreen_args = '-FullScreen -UseLocalCursor=0 -MenuKey F13' - self.execOnSelectedClients(""" -test -n "$EPOPTES_VNCVIEWER_PID" && kill $EPOPTES_VNCVIEWER_PID -export $(./get-display) -xset dpms force on -sleep 0.$((`hexdump -e '"%%d"' -n 2 /dev/urandom` %% 50 + 50)) -EPOPTES_VNCVIEWER_PID=$(./execute xvnc4viewer -Shared -ViewOnly %s $SERVER:%d)""" %(fullscreen_args, self.vncport), root=True) + self.execOnSelectedClients('stop_screensaver) + self.execOnSelectedClients('receive_broadcast %d %s' % (self.vncport, + fullscreen), root=True) def broadcastScreen(self, widget): - self._broadcastScreen(True) + self._broadcastScreen('true') def broadcastScreenWindowed(self, widget): - self._broadcastScreen(False) + self._broadcastScreen('') def stopTransmissions(self, widget): - # The vnc clients should automatically exit when the server is killed. - # Unfortunately, that isn't always true, so try to kill them anyway. - self.execOnClients(""" -test -n "$EPOPTES_VNCVIEWER_PID" && kill $EPOPTES_VNCVIEWER_PID -unset EPOPTES_VNCVIEWER_PID""", self.cstore, None, True) + self.execOnClients('stop_transmissions', self.cstore, None, True) if not self.vncserver is None: self.vncserver.kill() self.vncserver = None @@ -284,16 +262,39 @@ def execDialog(self, widget): if cmd[:5] == 'sudo ': as_root = True cmd = cmd[4:] - self.execOnSelectedClients(self.c.EXEC + cmd, root=as_root) + self.execOnSelectedClients('execute ' + cmd, root=as_root) - ## FIXME FIXME: Don't use zenity... + ## FIXME FIXME: Don't use zenity, use the message command instead... def sendMessageDialog(self, widget): cmd = startSendMessageDlg() if cmd != "": # Command is 'valid', execute on selected clients - as_root = False - self.execOnSelectedClients(self.c.EXEC + cmd, root=as_root) + self.execOnSelectedClients('execute ' + cmd) ## FIXME / FIXUS: Should we allow it? + def openTerminal(self, as_root): + clients = self.getSelectedClients() + + # If there is no client selected, send the command to all + if len(clients) == 0: + clients = self.cstore + + if as_root: + screen_params = "bash -l" + else: + screen_params = "-l" + + for client in clients: + inst = client[C_INSTANCE] + if inst.type == 'offline': + continue + + port = self.findUnusedPort() + + subprocess.Popen(['xterm', '-e', 'socat', + 'tcp-listen:%d,keepalive=1' % port, 'stdio,raw,echo=0']) + self.execOnClients('remote_term %d' % port, [client], + root=as_root) + def openUserTerminal(self, widget): self.openTerminal(False) @@ -301,40 +302,33 @@ def openRootTerminal(self, widget): self.openTerminal(True) def remoteRootTerminal(self, widget): - self.execOnSelectedClients(""" -export $(./get-display) -./execute xterm -e bash -l""", root=True) + self.execOnSelectedClients('root_term', root=True) ## END_FIXUS - # FIXME : Change the way lock screen works, don't kill and relock... def lockScreen(self, widget): """ Lock screen for all the selected clients, displaying a message """ msg = _("The screen is locked by a system administrator.") - self.execOnSelectedClients('test -n "$EPOPTES_LOCK_SCREEN_PID" && kill ' + \ - '"$EPOPTES_LOCK_SCREEN_PID"; EPOPTES_LOCK_SCREEN_PID=$(./execute ' + \ - './lock-screen %s)' %pipes.quote(msg)) + self.execOnSelectedClients('lock_screen 0 %s' % pipes.quote(msg)) def unlockScreen(self, widget): """ Unlock screen for all clients selected """ - self.execOnSelectedClients('''test -n "$EPOPTES_LOCK_SCREEN_PID" && ''' + \ - '''kill "$EPOPTES_LOCK_SCREEN_PID"; unset EPOPTES_LOCK_SCREEN_PID''') + self.execOnSelectedClients('unlock_screen') - # FIXME: Find something better def soundOff(self, widget): """ Disable sound usage for clients selected """ - self.execOnSelectedClients(self.c.EXEC_AMIXER + 'mute', root=True) + self.execOnSelectedClients('mute 0', root=True) def soundOn(self, widget): """ Enable sound usage for clients selected """ - self.execOnSelectedClients(self.c.EXEC_AMIXER + 'unmute', root=True) + self.execOnSelectedClients('unmute', root=True) def on_remove_from_group_clicked(self, widget): clients = self.getSelectedClients() @@ -416,11 +410,7 @@ def connected(self, daemon): # AMP callbacks def amp_clientConnected(self, handle): print "New connection from", handle - d = self.daemon.command(handle, u""" - VERSION=$(dpkg-query -W -f '${Version}' epoptes-client 2>/dev/null) - VERSION=${VERSION:-0.1} - echo "$USER\n$HOSTNAME\n$IP\n$MAC\n$TYPE\n$UID\n$VERSION\n$$" - """) + d = self.daemon.command(handle, u'info') d.addCallback(lambda r: self.addClient(handle, r)) d.addErrback(lambda err: self.printErrors("when connecting client %s: %s" %(handle, err))) @@ -457,11 +447,7 @@ def determine_offline(client): def amp_gotClients(self, handles): print "Got clients:", ', '.join(handles) or 'None' for handle in handles: - d = self.daemon.command(handle, u""" - VERSION=$(dpkg-query -W -f '${Version}' epoptes-client 2>/dev/null) - VERSION=${VERSION:-0.1} - echo "$USER\n$HOSTNAME\n$IP\n$MAC\n$TYPE\n$UID\n$VERSION\n$$" - """) + d = self.daemon.command(handle, u'info') d.addCallback(lambda r, h=handle: self.addClient(h, r, True)) d.addErrback(lambda err: self.printErrors("when enumerating client %s: %s" %(handle, err))) @@ -519,7 +505,7 @@ def getSelectedGroup(self): def addClient(self, handle, r, already=False): # already is True if the client was started before epoptes print "---\n**addClient's been called for", handle - try: + try: # TODO: properly parse the returned values user, host, ip, mac, type, uid, version, pid = r.strip().split() except: print "Can't extract client information, won't add this client" @@ -634,9 +620,9 @@ def askScreenshot(self, handle, firstTime=False): for client in self.cstore: if handle == client[C_SESSION_HANDLE]: timeoutID = gobject.timeout_add(10000, lambda h=handle: self.screenshotTimeout(h)) - self.execOnClients(self.c.SCREENSHOT + self.execOnClients('screenshot %d %d' % (self.scrWidth, self.scrHeight), handles=[handle], - reply=self.gotScreenshot, params=[timeoutID]) + reply=self.gotScreenshot, params=[timeoutID]) return False # That handle is no longer in the cstore, remove it try: del self.currentScreenshots[handle] @@ -822,40 +808,3 @@ def printErrors(self, error): print ' **Twisted error:', error return - def openTerminal(self, as_root): - clients = self.getSelectedClients() - - # If there is no client selected, send the command to all - if len(clients) == 0: - clients = self.cstore - - if as_root: - screen_params = "bash -l" - else: - screen_params = "-l" - - for client in clients: - inst = client[C_INSTANCE] - if inst.type == 'offline': - continue - elif inst.type == 'thin' and not as_root: - server = "127.0.0.1" - else: - server = "server" - - port = self.findUnusedPort() - - subprocess.Popen(['xterm', '-e', 'socat', - 'tcp-listen:%d,keepalive=1' % port, 'stdio,raw,echo=0']) - self.execOnClients(("""./execute sh -c "cd; sleep 1; """ + - """TERM=xterm exec socat SYSTEM:'exec screen %s',pty,""" + - """stderr tcp:$SERVER:%d" """) % (screen_params, port), - [client], root=as_root) - - def execInTerminal(self, widget, command): - name = widget.get_child().get_text() - subprocess.Popen([ 'x-terminal-emulator', '-e', 'sh', '-c', command - + ' && read -p "Script \'%s\' finished succesfully. Press [Enter] to close this window." dummy' % name - + ' || read -p "Script \'%s\' finished with errors. Press [Enter] to close this window." dummy' % name] - ) - diff --git a/setup.py b/setup.py index 8fe2a23..d2b39c4 100755 --- a/setup.py +++ b/setup.py @@ -75,7 +75,7 @@ def subtract_files(a, b): name='epoptes', version = changelog_version(), description = 'Computer lab administration and monitoring tool', - url = 'https://launchpad.net/epoptes', + url = 'http://www.epoptes.org', license = 'GNU GPL v3', author = 'Fotis Tsamis', author_email = 'ftsamis@gmail.com',