1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 __NAME__ = 'x2goxserver-pylib'
27
28 from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
29 if _X2GOCLIENT_OS == 'Windows':
30 import wmi
31 import win32process
32
33
34 import os
35 import threading
36 import gevent
37 import copy
38
39
40 import log
41 from defaults import X2GO_XCONFIG_CONFIGFILES as _X2GO_XCONFIG_CONFIGFILES
42 from defaults import X2GO_CLIENTXCONFIG_DEFAULTS as _X2GO_CLIENTXCONFIG_DEFAULTS
43 import inifiles
44 import utils
47 """\
48 Configuration file based XServer startup settings for X2GoClient instances.
49
50 This class is needed for Windows systems and (maybe soon) for Unix desktops using Wayland.
51
52 """
53
54 - def __init__(self, config_files=_X2GO_XCONFIG_CONFIGFILES, defaults=_X2GO_CLIENTXCONFIG_DEFAULTS, logger=None, loglevel=log.loglevel_DEFAULT):
55 """\
56 Constructs an L{X2GoClientXConfig} instance. This is normally done by an L{X2GoClient} instance.
57 You can retrieve this L{X2GoClientXConfig} instance with the C{X2GoClient.get_client_xconfig()}
58 method.
59
60 On construction the L{X2GoClientXConfig} instance is filled with values from the configuration files::
61
62 /etc/x2goclient/xconfig
63 ~/.x2goclient/xconfig
64
65 The files are read in the specified order and config options of both files are merged. Options
66 set in the user configuration file (C{~/.x2goclient/xconfig}) override global options set in
67 C{/etc/x2goclient/xconfig}.
68
69 @param config_files: a list of configuration file names
70 @type config_files: C{list}
71 @param defaults: a Python dictionary with configuration file defaults (use on your own risk)
72 @type defaults: C{dict}
73 @param logger: you can pass an L{X2GoLogger} object to the L{X2GoClientXConfig} constructor
74 @type logger: C{obj}
75 @param loglevel: if no L{X2GoLogger} object has been supplied a new one will be
76 constructed with the given loglevel
77 @type loglevel: C{int}
78
79 """
80 if _X2GOCLIENT_OS not in ("Windows"):
81 import exceptions
82 class OSNotSupportedException(exceptions.StandardError): pass
83 raise OSNotSupportedException('classes of x2go.xserver module are for Windows only')
84
85 inifiles.X2GoIniFile.__init__(self, config_files, defaults=defaults, logger=logger, loglevel=loglevel)
86
87 _known_xservers = utils.merge_ordered_lists(self.defaultValues['XServers']['known_xservers'], self.known_xservers)
88
89 if _known_xservers != self.known_xservers:
90 self.update_value('XServers', 'known_xservers', _known_xservers)
91 self.write_user_config = True
92 self.write()
93
95 """\
96 Store the Xserver configuration to the storage backend (i.e. on disk).
97
98 For writing the first of the C{config_files} specified on instance construction
99 that is writable will be used.
100
101 @return: C{True} if the user config file has been successfully written, C{False} otherwise.
102 @rtype: C{bool}
103
104 """
105 self._write_user_config = self.write_user_config
106 return self._X2GoIniFile__write()
107
109 """\
110 Retrieve the XServer configuration (from the xconfig file) for the given XServer application.
111
112 @param xserver_name: name of the XServer application
113 @type xserver_name: C{str}
114
115 @return: A Python dictionary containing the XServer's configuration settings
116 @rtype: C{list}
117
118 """
119 _xserver_config = {}
120 for option in self.iniConfig.options(xserver_name):
121 try:
122 _xserver_config[option] = self.get(xserver_name, option, key_type=self.get_type(xserver_name, option))
123 except KeyError:
124 pass
125 return _xserver_config
126
127 @property
129 """\
130 Renders a list of XServers that are known to Python X2Go.
131
132 """
133 return self.get_value('XServers', 'known_xservers')
134
135 @property
137 """\
138 Among the known XServers renders a list of XServers that are actually
139 installed on the system.
140
141 """
142 _installed = []
143 for xserver_name in self.known_xservers:
144 if os.path.exists(os.path.normpath(self.get_xserver_config(xserver_name)['test_installed'])):
145 _installed.append(xserver_name)
146 return _installed
147
148 @property
150 """\
151 Tries to render a list of running XServer processes from the system's process list.
152
153 """
154 _running = []
155 _wmi = wmi.WMI()
156 _my_wmi_sessionid = [ _p.SessionId for _p in _wmi.Win32_Process() if _p.ProcessId == os.getpid() ][0]
157
158 _process_list = _wmi.Win32_Process()
159 for xserver_name in self.installed_xservers:
160 process_name = self.get_xserver_config(xserver_name)['process_name']
161 if [ _p.Name for _p in _process_list if _p.Name == process_name and _p.SessionId == _my_wmi_sessionid ]:
162
163 _running.append(xserver_name)
164 continue
165 return _running
166
167 @property
169 """\
170 Detect if there is an XServer (that is known to Python X2Go) installed on the system.
171 Equals C{True} if we have found an installed XServer that we can launch.
172
173 """
174 return bool(self.installed_xservers)
175
176 @property
178 """\
179 Detect if an XServer launch is really needed (or if we use an already running XServer instance).
180 Equals C{True} if we have to launch an XServer before we can start/resume
181 X2Go sessions.
182
183 """
184 return not bool(self.running_xservers)
185
186 @property
199
200 @property
202 """\
203 Returns the list of preferred XServer names (most preferred first).
204
205 """
206 return self.installed_xservers
207
209 """\
210 Get an unused TCP/IP port for the to-be-launched X server and write it
211 to the user's X configuration file.
212
213 @param xserver_name: name of the XServer application
214 @type xserver_name: C{str}
215
216 """
217 _default_display = self.get_xserver_config(xserver_name)['display']
218 _last_display = self.get_xserver_config(xserver_name)['last_display']
219
220 try:
221 _default_xserver_port = int(_default_display.split(":")[1].split(".")[0]) + 6000
222 _last_xserver_port = int(_last_display.split(":")[1].split(".")[0]) + 6000
223
224
225 if utils.detect_unused_port(preferred_port=_last_xserver_port) == _last_xserver_port:
226 _detect_xserver_port = _last_xserver_port
227
228
229 elif utils.detect_unused_port(preferred_port=_default_xserver_port) == _default_xserver_port:
230 _detect_xserver_port = _default_xserver_port
231
232
233 else:
234 _xserver_port = _default_xserver_port +1
235 self.logger('Attempting to detect an unused TCP/IP port for our X-Server, starting with port %s' % _xserver_port, loglevel=log.loglevel_DEBUG)
236 while utils.detect_unused_port(preferred_port=_xserver_port) != _xserver_port:
237 _xserver_port += 1
238 self.logger('TCP/IP port was in use, trying next port: %s' % _xserver_port, loglevel=log.loglevel_DEBUG)
239 self.logger('allocating TCP/IP port %s for our X-Server' % _xserver_port, loglevel=log.loglevel_DEBUG)
240 _detect_xserver_port = _xserver_port
241
242
243 if _detect_xserver_port != _last_xserver_port:
244 _new_display = _last_display.replace(str(_last_xserver_port -6000), str(_detect_xserver_port -6000))
245 self.logger('cannot use configured X DISPLAY, the new available DISPLAY port %s has been detected' % _new_display, loglevel=log.loglevel_NOTICE)
246 self.update_value(xserver_name, 'last_display', _new_display)
247 _parameters = self.get_value(xserver_name, 'parameters')
248 _parameters[0] = ":%s" % (_detect_xserver_port -6000)
249 self.update_value(xserver_name, 'parameters', tuple(_parameters))
250 self.write_user_config = True
251 self.write()
252 return _new_display
253
254 return _last_display
255
256 except TypeError:
257 pass
258
261 """
262 This class is responsible for starting/stopping an external XServer application.
263
264 X2Go applications require a running XServer on the client system. This class will
265 manage/handle the XServer while your X2Go application is running.
266
267 """
269 """\
270 Initialize an XServer thread.
271
272 @param xserver_name: name of the XServer to start (refer to the xconfig file for available names)
273 @type xserver_name: C{str}
274 @param xserver_config: XServer configuration node (as derived from L{X2GoClientXConfig.get_xserver_config()}
275 @type xserver_config: C{dict}
276 @param logger: you can pass an L{X2GoLogger} object to the L{X2GoClientXConfig} constructor
277 @type logger: C{obj}
278 @param loglevel: if no L{X2GoLogger} object has been supplied a new one will be
279 constructed with the given loglevel
280 @type loglevel: C{int}
281
282 """
283 if _X2GOCLIENT_OS not in ("Windows"):
284 import exceptions
285 class OSNotSupportedException(exceptions.StandardError): pass
286 raise OSNotSupportedException('classes of x2go.xserver module are for Windows only')
287
288 if logger is None:
289 self.logger = log.X2GoLogger(loglevel=loglevel)
290 else:
291 self.logger = copy.deepcopy(logger)
292 self.logger.tag = __NAME__
293
294 self._keepalive = None
295
296 self.xserver_name = xserver_name
297 self.xserver_config = xserver_config
298 self.hProcess = None
299
300 if self.xserver_config.has_key('last_display'):
301
302 self.logger('setting DISPLAY environment variable to %s' % self.xserver_config['last_display'], loglevel=log.loglevel_NOTICE)
303 os.environ.update({'DISPLAY': str(self.xserver_config['last_display'])})
304 threading.Thread.__init__(self)
305 self.daemon = True
306 self.start()
307
309 """\
310 Class destructor. Terminate XServer process.
311
312 """
313 self._terminate_xserver()
314
316 """\
317 Start this L{X2GoXServer} thread. This will launch the configured XServer application.
318
319 """
320 self._keepalive = True
321 cmd_line = [self.xserver_config['run_command']]
322 cmd_line.extend(self.xserver_config['parameters'])
323 self.logger('starting XServer ,,%s\'\' with command line: %s' % (self.xserver_name, ' '.join(cmd_line)), loglevel=log.loglevel_DEBUG)
324
325 if _X2GOCLIENT_OS == 'Windows':
326 si = win32process.STARTUPINFO()
327 p_info = win32process.CreateProcess(None,
328 ' '.join(cmd_line),
329 None,
330 None,
331 0,
332 win32process.NORMAL_PRIORITY_CLASS,
333 None,
334 None,
335 si,
336 )
337 (self.hProcess, hThread, processId, threadId) = p_info
338
339 while self._keepalive:
340 gevent.sleep(1)
341
342 self._terminate_xserver()
343
345 """\
346 Terminate the runnint XServer process.
347
348 """
349 self.logger('terminating running XServer ,,%s\'\'' % self.xserver_name, loglevel=log.loglevel_DEBUG)
350
351 if _X2GOCLIENT_OS == 'Windows' and self.hProcess is not None:
352 try:
353 win32process.TerminateProcess(self.hProcess, 0)
354 except win32process.error:
355 self.logger('XServer ,,%s\'\' could not be terminated.' % self.xserver_name, loglevel=log.loglevel_DEBUG)
356
358 """\
359 A call to this method will stop the XServer application and do a cleanup afterwards.
360
361 """
362 self._keepalive = False
363 self.logger('stop_thread() method has been called', loglevel=log.loglevel_DEBUG)
364