source: epoptes/trunk/fuentes/epoptes-client/remote-assistance @ 295

Last change on this file since 295 was 295, checked in by mabarracus, 4 years ago

copy trusty epoptes code

  • Property svn:executable set to *
File size: 8.8 KB
Line 
1#!/usr/bin/python
2#-*- coding: utf-8 -*-
3###########################################################################
4# Remote assistance
5#
6# Copyright (C) 2012 Fotis Tsamis <ftsamis@gmail.com>
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FINESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20#
21# On Debian GNU/Linux systems, the complete text of the GNU General
22# Public License can be found in `/usr/share/common-licenses/GPL".
23###########################################################################
24
25def split_hostname(hostname):
26    """Returns a 2-tuple containing the IP address and the port number extracted
27    from hostname. If there is no port specified, 5500 will be returned.
28    """
29    if ':' in hostname:
30        hostname, port = hostname.split(':', 1)
31        port = int(port)
32    else:
33        port = 5500
34    return (hostname, port)
35
36import sys
37import os
38if __name__ == '__main__' and len(sys.argv) > 1:
39    os.execl("/bin/sh", "sh", "-c", """socat tcp:%s:%d SYSTEM:"sleep 1; exec screen -xRR ra$$",pty,stderr & exec screen -S ra$$""" % split_hostname(sys.argv[1]))
40import signal
41import subprocess
42import gtk
43import gobject
44import gettext
45gettext.install('epoptes', unicode=True)
46import locale
47locale.textdomain('epoptes')
48
49pid = os.getpid()
50#os.chdir('/usr/share/epoptes-client')
51
52class Support:
53    def __init__(self):
54        self.wTree = gtk.Builder()
55        self.wTree.add_from_file('remote_assistance.ui')
56        self.get = self.wTree.get_object
57       
58        signal.signal(signal.SIGUSR1, self.connected)
59        self.wTree.connect_signals(self)
60       
61        self.status_icon = self.get('status_icon')
62        self.status_label = self.get('status_label')
63        self.method_combo = self.get('method_combobox')
64        self.action_button = self.get('connect_button')
65        self.status_box = self.get('status_hbox')
66        self.action_signal_handler = self.action_button.connect('clicked', self.connect)
67        self.spinner = gtk.Spinner()
68        self.spinner.set_size_request(16, 16)
69        self.status_box.pack_start(self.spinner, False)
70        self.status_box.reorder_child(self.spinner, 0)
71       
72        self.retry_timeout_id = None
73       
74        self.retry = False
75        self.retry_interval = 10
76        self.manually_stopped = False
77       
78        self.proc = None
79        self.host = ''
80        self.connected = False
81       
82        self.get('support_dialog').show()
83       
84       
85    def connect(self, widget=None):
86        """Gets called when the user presses Connect button"""
87       
88        self.host = self.get('host_entry').get_text().strip()
89        ip, port = split_hostname(self.host)
90       
91        vnc = self.method_combo.get_active() == 0
92        if vnc:
93            cmd = ['x11vnc', '-q', '-nopw', '-connect_or_exit', '%s:%d' % (ip, port), '-afteraccept', 'kill -USR1 %d' % pid]
94        else:
95            cmd = ['socat', 'tcp:%s:%d' % (ip, port), 
96                'SYSTEM:"{ kill -USR1 %d; sleep 1; xterm -e screen -xRR ra$$; } & exec screen -S ra$$",pty,stderr' % pid]
97       
98        self.proc = subprocess.Popen(cmd)
99       
100        # Set the status as "Connecting"
101        if self.retry_timeout_id:
102            gobject.source_remove(self.retry_timeout_id)
103            self.retry_timeout_id = None
104        self.set_state('connecting')
105       
106        # Start polling the process every 1 second to see if it's still alive
107        gobject.timeout_add(1000, self.poll_process)
108   
109    def disconnect(self, widget=None):
110        self.manually_stopped = True
111        if self.retry_timeout_id is not None:
112            self.set_state('disconnected')
113            gobject.source_remove(self.retry_timeout_id)
114            self.retry_timeout_id = None
115        if self.proc:
116            self.proc.kill()
117        self.set_action_button('connect')
118   
119    def poll_process(self):
120        # if process has not terminated yet return True to continue the timeout
121        if self.proc.poll() is None:
122            return True
123        else:
124            # Check if it's a disconnect or a failure and call the correct signal
125            if self.connected:
126                self.disconnected()
127                self.connected = False
128            else:
129                self.failed()
130            self.proc = None
131            return False
132   
133   
134    #=== Process signal handlers start ===#
135   
136    def connected(self, signum, frame):
137        """Gets called when the program receives SIGUSR1 (succesful connection)"""
138        self.set_state('connected')
139   
140    def disconnected(self):
141        """Gets called when the process is terminated but a successful connection
142        has taken place"""
143        self.set_state('disconnected')
144        if self.retry and not self.manually_stopped:
145            self.update_and_retry(_('Not connected'), self.retry_interval)
146            self.set_action_button('disconnect')
147            self.manually_stopped = True
148        else:
149            self.set_action_button('connect')
150   
151    def failed(self):
152        """Gets called when the process is terminated but there wasn't any
153        successful connection"""
154        self.set_state('failed')
155   
156    #=== Process signal handlers end ===#
157   
158   
159    def on_close_button_clicked(self, widget):
160        """Gets called when the user clicks the Close button"""
161        if self.proc:
162            self.disconnect()
163        exit()
164   
165    def on_dialog_delete_event(self, widget, event):
166        self.on_close_button_clicked(None)
167   
168    def host_changed(self, widget):
169        txt = widget.get_text().strip()
170        if self.action_button.get_label() == gtk.STOCK_CONNECT:
171                self.action_button.set_property('sensitive', len(txt) > 0)
172   
173    def set_state(self, state):
174        self.stop_spinner()
175       
176        if state == 'connecting':
177            self.start_spinner()
178            self.status_label.set_text(_('Connecting to %s...') %self.host)
179            self.set_action_button('disconnect')
180        elif state == 'connected':
181            self.connected = True
182            if self.method_combo.get_active() == 0:
183                mode = 'VNC'
184            else:
185                mode = 'console'
186            self.status_icon.set_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_BUTTON)
187            self.status_label.set_text(_('Connected to %s') % self.host)
188            self.set_action_button('disconnect')
189        elif state == 'disconnected':
190            msg = _('Not connected')
191            self.status_icon.set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_BUTTON)
192            self.status_label.set_text(msg)
193       
194        elif state == 'failed':
195            msg = _('Failed to connect to %s') %self.host
196            self.status_label.set_text(msg)
197            self.status_icon.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_BUTTON)
198           
199            if self.retry:
200                self.update_and_retry(msg, self.retry_interval)
201                self.set_action_button('disconnect')
202            else:
203                self.set_action_button('connect')
204   
205    def update_and_retry(self, msg, interval):
206        if interval == 0:
207            self.connect()
208        else:
209            self.status_label.set_text(msg+' '+_('Retrying in %d...') % interval)
210            self.retry_timeout_id = gobject.timeout_add(1000, self.update_and_retry, msg, interval-1)
211        return False
212   
213    def set_action_button(self, mode):
214        """Change the Connect button to Disconnect and vice versa,
215        according to mode and set the correct signal."""
216       
217        callback = None
218        btn = self.action_button
219        if mode == 'connect':
220            btn.set_label(gtk.STOCK_CONNECT)
221            callback = self.connect
222        elif mode == 'disconnect':
223            btn.set_label(gtk.STOCK_DISCONNECT)
224            callback = self.disconnect
225       
226        if callback:
227            btn.disconnect(self.action_signal_handler)
228            self.action_signal_handler = btn.connect('clicked', callback)
229   
230    def start_spinner(self):
231        """Replace the status icon with a running spinner"""
232        self.status_icon.hide()
233        self.spinner.show()
234        self.spinner.start()
235   
236    def stop_spinner(self):
237        """Replace the spinner with the status icon"""
238        self.spinner.stop()
239        self.spinner.hide()
240        self.status_icon.show()
241       
242    def toggle_retry(self, widg):
243        self.retry = not self.retry
244
245       
246if __name__ == '__main__':
247    Support()
248    gtk.main()
Note: See TracBrowser for help on using the repository browser.