1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """\
21 X2goControlSessionSTDOUT class - core functions for handling your individual X2Go sessions.
22
23 This backend handles X2Go server implementations that respond via server-side STDOUT.
24
25 """
26 __NAME__ = 'x2gocontrolsession-pylib'
27
28
29 import os
30 import types
31 import paramiko
32 import gevent
33 import copy
34 import string
35 import random
36 import re
37 import locale
38 import threading
39 import cStringIO
40
41 from gevent import socket
42
43
44 import x2go.sshproxy as sshproxy
45 import x2go.log as log
46 import x2go.utils as utils
47 import x2go.x2go_exceptions as x2go_exceptions
48 import x2go.defaults as defaults
49 import x2go.checkhosts as checkhosts
50
51 from x2go.backends.terminal import X2goTerminalSession as _X2goTerminalSession
52 from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo
53 from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList
54 from x2go.backends.proxy import X2goProxy as _X2goProxy
55
56 from x2go.monkey_patch_paramiko import monkey_patch_paramiko
57 monkey_patch_paramiko()
60 """\
61 In command strings X2Go server scripts expect blanks being rewritten to ,,X2GO_SPACE_CHAR''.
62 Commands get rewritten in the terminal sessions. This re-rewrite function helps
63 displaying command string in log output.
64
65 @param cmd: command that has to be rewritten for log output
66 @type cmd: C{str}
67
68 @return: the command with ,,X2GO_SPACE_CHAR'' re-replaced by blanks
69 @rtype: C{str}
70
71 """
72
73 if cmd:
74 cmd = cmd.replace("X2GO_SPACE_CHAR", " ")
75 return cmd
76
78 """\
79 In command strings Python X2Go replaces some macros with actual values:
80
81 - X2GO_USER -> the user name under which the user is authenticated via SSH
82 - X2GO_PASSWORD -> the password being used for SSH authentication
83
84 Both macros can be used to on-the-fly authenticate via RDP.
85
86 @param cmd: command that is to be sent to an X2Go server script
87 @type cmd: C{str}
88 @param user: the SSH authenticated user name
89 @type password: the password being used for SSH authentication
90
91 @return: the command with macros replaced
92 @rtype: C{str}
93
94 """
95
96
97 if cmd and user:
98 cmd = cmd.replace('X2GO_USER', user)
99
100
101 if cmd and password:
102 cmd = cmd.replace('X2GO_PASSWORD', password)
103 return cmd
104
107 """\
108 In the Python X2Go concept, X2Go sessions fall into two parts: a control session and one to many terminal sessions.
109
110 The control session handles the SSH based communication between server and client. It is mainly derived from
111 C{paramiko.SSHClient} and adds on X2Go related functionality.
112
113 """
114 associated_terminals = None
115
116 - def __init__(self,
117 profile_name='UNKNOWN',
118 add_to_known_hosts=False,
119 known_hosts=None,
120 terminal_backend=_X2goTerminalSession,
121 info_backend=_X2goServerSessionInfo,
122 list_backend=_X2goServerSessionList,
123 proxy_backend=_X2goProxy,
124 client_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_CLIENT_ROOTDIR),
125 sessions_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_SESSIONS_ROOTDIR),
126 ssh_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_SSH_ROOTDIR),
127 logger=None, loglevel=log.loglevel_DEFAULT,
128 published_applications_no_submenus=0,
129 **kwargs):
130 """\
131 Initialize an X2Go control session. For each connected session profile there will be one SSH-based
132 control session and one to many terminal sessions that all server-client-communicate via this one common control
133 session.
134
135 A control session normally gets set up by an L{X2goSession} instance. Do not use it directly!!!
136
137 @param profile_name: the profile name of the session profile this control session works for
138 @type profile_name: C{str}
139 @param add_to_known_hosts: Auto-accept server host validity?
140 @type add_to_known_hosts: C{bool}
141 @param known_hosts: the underlying Paramiko/SSH systems C{known_hosts} file
142 @type known_hosts: C{str}
143 @param terminal_backend: X2Go terminal session backend to use
144 @type terminal_backend: C{class}
145 @param info_backend: backend for handling storage of server session information
146 @type info_backend: C{X2goServerSessionInfo*} instance
147 @param list_backend: backend for handling storage of session list information
148 @type list_backend: C{X2goServerSessionList*} instance
149 @param proxy_backend: backend for handling the X-proxy connections
150 @type proxy_backend: C{X2goProxy*} instance
151 @param client_rootdir: client base dir (default: ~/.x2goclient)
152 @type client_rootdir: C{str}
153 @param sessions_rootdir: sessions base dir (default: ~/.x2go)
154 @type sessions_rootdir: C{str}
155 @param ssh_rootdir: ssh base dir (default: ~/.ssh)
156 @type ssh_rootdir: C{str}
157 @param published_applications_no_submenus: published applications menus with less items than C{published_applications_no_submenus}
158 are rendered without submenus
159 @type published_applications_no_submenus: C{int}
160 @param logger: you can pass an L{X2goLogger} object to the
161 L{X2goControlSessionSTDOUT} constructor
162 @type logger: L{X2goLogger} instance
163 @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
164 constructed with the given loglevel
165 @type loglevel: C{int}
166 @param kwargs: parameters passed through to C{SSHClient.__init__()}
167 @type kwargs: C{dict}
168
169 """
170 self.associated_terminals = {}
171 self.terminated_terminals = []
172
173 self.profile_name = profile_name
174 self.add_to_known_hosts = add_to_known_hosts
175 self.known_hosts = known_hosts
176
177 self.hostname = None
178 self.port = None
179
180 self.sshproxy_session = None
181
182 self._session_auth_rsakey = None
183 self._remote_home = None
184 self._remote_group = {}
185 self._remote_username = None
186 self._remote_peername = None
187
188 self._server_features = None
189
190 if logger is None:
191 self.logger = log.X2goLogger(loglevel=loglevel)
192 else:
193 self.logger = copy.deepcopy(logger)
194 self.logger.tag = __NAME__
195
196 self._terminal_backend = terminal_backend
197 self._info_backend = info_backend
198 self._list_backend = list_backend
199 self._proxy_backend = proxy_backend
200
201 self.client_rootdir = client_rootdir
202 self.sessions_rootdir = sessions_rootdir
203 self.ssh_rootdir = ssh_rootdir
204
205 self._published_applications_menu = {}
206
207 paramiko.SSHClient.__init__(self, **kwargs)
208 if self.add_to_known_hosts:
209 self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
210
211 self.session_died = False
212
213 self.published_applications_no_submenus = published_applications_no_submenus
214 self._already_querying_published_applications = False
215
216 self._transport_lock = threading.Lock()
217
219 """\
220 Get the hostname as stored in the properties of this control session.
221
222 @return: the hostname of the connected X2Go server
223 @rtype: C{str}
224
225 """
226 return self.hostname
227
229 """\
230 Get the port number of the SSH connection as stored in the properties of this control session.
231
232 @return: the server-side port number of the control session's SSH connection
233 @rtype: C{str}
234
235 """
236 return self.port
237
239 """\
240 Load known SSH host keys from the C{known_hosts} file.
241
242 If the file does not exist, create it first.
243
244 """
245 if self.known_hosts is not None:
246 utils.touch_file(self.known_hosts)
247 self.load_host_keys(self.known_hosts)
248
250 """\
251 On instance descruction, do a proper session disconnect from the server.
252
253 """
254 self.disconnect()
255
257 """
258 Put a local file on the remote server via sFTP.
259
260 During sFTP operations, remote command execution gets blocked.
261
262 @param local_path: full local path name of the file to be put on the server
263 @type local_path: C{str}
264 @param remote_path: full remote path name of the server-side target location, path names have to be Unix-compliant
265 @type remote_path: C{str}
266
267 @raise X2goControlSessionException: if the SSH connection dropped out
268
269 """
270 ssh_transport = self.get_transport()
271 self._transport_lock.acquire()
272 if ssh_transport and ssh_transport.is_authenticated():
273 self.logger('sFTP-put: %s -> %s:%s' % (os.path.normpath(local_path), self.remote_peername(), remote_path), loglevel=log.loglevel_DEBUG)
274 self.sftp_client = paramiko.SFTPClient.from_transport(ssh_transport)
275 try:
276 self.sftp_client.put(os.path.normpath(local_path), remote_path)
277 except (x2go_exceptions.SSHException, socket.error, IOError):
278
279 self.session_died = True
280 self._transport_lock.release()
281 raise x2go_exceptions.X2goControlSessionException('The SSH connection was dropped during an sFTP put action.')
282 self.sftp_client = None
283 self._transport_lock.release()
284
286 """
287 Create a text file on the remote server via sFTP.
288
289 During sFTP operations, remote command execution gets blocked.
290
291 @param remote_path: full remote path name of the server-side target location, path names have to be Unix-compliant
292 @type remote_path: C{str}
293 @param content: a text file, multi-line files use Unix-link EOL style
294 @type content: C{str}
295
296 @raise X2goControlSessionException: if the SSH connection dropped out
297
298 """
299 ssh_transport = self.get_transport()
300 self._transport_lock.acquire()
301 if ssh_transport and ssh_transport.is_authenticated():
302 self.logger('sFTP-write: opening remote file %s on host %s for writing' % (remote_path, self.remote_peername()), loglevel=log.loglevel_DEBUG)
303 self.sftp_client = paramiko.SFTPClient.from_transport(ssh_transport)
304 try:
305 remote_fileobj = self.sftp_client.open(remote_path, 'w')
306 self.logger('sFTP-write: writing content: %s' % content, loglevel=log.loglevel_DEBUG_SFTPXFER)
307 remote_fileobj.write(content)
308 remote_fileobj.close()
309 except (x2go_exceptions.SSHException, socket.error, IOError):
310 self.session_died = True
311 self._transport_lock.release()
312 self.logger('sFTP-write: opening remote file %s on host %s failed' % (remote_path, self.remote_peername()), loglevel=log.loglevel_WARN)
313 raise x2go_exceptions.X2goControlSessionException('The SSH connection was dropped during an sFTP write action.')
314 self.sftp_client = None
315 self._transport_lock.release()
316
318 """
319 Remote a remote file from the server via sFTP.
320
321 During sFTP operations, remote command execution gets blocked.
322
323 @param remote_path: full remote path name of the server-side file to be removed, path names have to be Unix-compliant
324 @type remote_path: C{str}
325
326 @raise X2goControlSessionException: if the SSH connection dropped out
327
328 """
329 ssh_transport = self.get_transport()
330 self._transport_lock.acquire()
331 if ssh_transport and ssh_transport.is_authenticated():
332 self.logger('sFTP-write: removing remote file %s on host %s' % (remote_path, self.remote_peername()), loglevel=log.loglevel_DEBUG)
333 self.sftp_client = paramiko.SFTPClient.from_transport(ssh_transport)
334 try:
335 self.sftp_client.remove(remote_path)
336 except (x2go_exceptions.SSHException, socket.error, IOError):
337 self.session_died = True
338 self._transport_lock.release()
339 self.logger('sFTP-write: removing remote file %s on host %s failed' % (remote_path, self.remote_peername()), loglevel=log.loglevel_WARN)
340 raise x2go_exceptions.X2goControlSessionException('The SSH connection was dropped during an sFTP remove action.')
341 self.sftp_client = None
342 self._transport_lock.release()
343
345 """
346 Execute an X2Go server-side command via SSH.
347
348 During SSH command executions, sFTP operations get blocked.
349
350 @param cmd_line: the command to be executed on the remote server
351 @type cmd_line: C{str} or C{list}
352 @param loglevel: use this loglevel for reporting about remote command execution
353 @type loglevel: C{int}
354 @param timeout: if commands take longer than C{<timeout>} to be executed, consider the control session connection
355 to have died.
356 @type timeout: C{int}
357 @param kwargs: parameters that get passed through to the C{paramiko.SSHClient.exec_command()} method.
358 @type kwargs: C{dict}
359
360 @return: C{True} if the command could be successfully executed on the remote X2Go server
361 @rtype: C{bool}
362
363 @raise X2goControlSessionException: if the command execution failed (due to a lost connection)
364
365 """
366 if type(cmd_line) == types.ListType:
367 cmd = " ".join(cmd_line)
368 else:
369 cmd = cmd_line
370
371 if self.session_died:
372 self.logger("control session seams to be dead, not executing command ,,%s'' on X2Go server %s" % (_rerewrite_blanks(cmd), self.profile_name,), loglevel=loglevel)
373 return (cStringIO.StringIO(), cStringIO.StringIO(), cStringIO.StringIO('failed to execute command'))
374
375 self._transport_lock.acquire()
376
377 _retval = None
378
379 ssh_transport = self.get_transport()
380 if ssh_transport and ssh_transport.is_authenticated():
381
382 timer = gevent.Timeout(timeout)
383 timer.start()
384 try:
385 self.logger("executing command on X2Go server ,,%s'': %s" % (self.profile_name, _rerewrite_blanks(cmd)), loglevel=loglevel)
386 _retval = self.exec_command(_rewrite_password(cmd, user=self.get_transport().get_username(), password=self._session_password), **kwargs)
387 except AttributeError:
388 self.session_died = True
389 self._transport_lock.release()
390 if self.sshproxy_session:
391 self.sshproxy_session.stop_thread()
392 raise x2go_exceptions.X2goControlSessionException('the X2Go control session has died unexpectedly')
393 except EOFError:
394 self.session_died = True
395 self._transport_lock.release()
396 if self.sshproxy_session:
397 self.sshproxy_session.stop_thread()
398 raise x2go_exceptions.X2goControlSessionException('the X2Go control session has died unexpectedly')
399 except x2go_exceptions.SSHException:
400 self.session_died = True
401 self._transport_lock.release()
402 if self.sshproxy_session:
403 self.sshproxy_session.stop_thread()
404 raise x2go_exceptions.X2goControlSessionException('the X2Go control session has died unexpectedly')
405 except gevent.timeout.Timeout:
406 self.session_died = True
407 self._transport_lock.release()
408 if self.sshproxy_session:
409 self.sshproxy_session.stop_thread()
410 raise x2go_exceptions.X2goControlSessionException('the X2Go control session command timed out')
411 except socket.error:
412 self.session_died = True
413 self._transport_lock.release()
414 if self.sshproxy_session:
415 self.sshproxy_session.stop_thread()
416 raise x2go_exceptions.X2goControlSessionException('the X2Go control session has died unexpectedly')
417 finally:
418 timer.cancel()
419
420 else:
421 self._transport_lock.release()
422 raise x2go_exceptions.X2goControlSessionException('the X2Go control session is not connected')
423
424 self._transport_lock.release()
425 return _retval
426
427 @property
429 """\
430 Render a list of server-side X2Go features. Results get cached once there has been one successfull query.
431
432 """
433 if self._server_features is None:
434 (stdin, stdout, stderr) = self._x2go_exec_command('which x2gofeaturelist >/dev/null && x2gofeaturelist')
435 self._server_features = stdout.read().split('\n')
436 self.logger('server-side X2Go features are: %s' % self._server_features, loglevel=log.loglevel_DEBUG)
437 return self._server_features
438 else:
439 return self._server_features
440
442 """\
443 Do a query for the server-side list of X2Go features.
444
445 @param force: do not use the cached feature list, really ask the server (again)
446 @type force: C{bool}
447
448 @return: list of X2Go feature names
449 @rtype: C{list}
450
451 """
452 if force:
453 self._server_features = None
454 return self._x2go_server_features
455 get_server_features = query_server_features
456
457 @property
459 """\
460 Retrieve and cache the remote home directory location.
461
462 """
463 if self._remote_home is None:
464 (stdin, stdout, stderr) = self._x2go_exec_command('echo $HOME')
465 stdout_r = stdout.read()
466 if stdout_r:
467 self._remote_home = stdout_r.split()[0]
468 self.logger('remote user\' home directory: %s' % self._remote_home, loglevel=log.loglevel_DEBUG)
469 return self._remote_home
470 else:
471 return self._remote_home
472
474 """\
475 Retrieve and cache the members of a server-side POSIX group.
476
477 @param group: remote POSIX group name
478 @type group: C{str}
479
480 @return: list of POSIX group members
481 @rtype: C{list}
482
483 """
484 if not self._remote_group.has_key(group):
485 (stdin, stdout, stderr) = self._x2go_exec_command('getent group %s | cut -d":" -f4' % group)
486 self._remote_group[group] = stdout.read().split('\n')[0].split(',')
487 self.logger('remote %s group: %s' % (group, self._remote_group[group]), loglevel=log.loglevel_DEBUG)
488 return self._remote_group[group]
489 else:
490 return self._remote_group[group]
491
493 """\
494 Is the remote user allowed to launch X2Go sessions?
495
496 FIXME: this method is currently non-functional.
497
498 @param username: remote user name
499 @type username: C{str}
500
501 @return: C{True} if the remote user is allowed to launch X2Go sessions
502 @rtype: C{bool}
503
504 """
505
506
507
508
509
510
511 return True
512
514 """\
515 Check if the remote user is allowed to use SSHFS mounts.
516
517 @return: C{True} if the user is allowed to connect client-side shares to the X2Go session
518 @rtype: C{bool}
519
520 """
521 if self.remote_username() in self._x2go_remote_group('fuse'):
522 return True
523 return False
524
526 """\
527 Returns (and caches) the control session's remote username.
528
529 @return: SSH transport's user name
530 @rtype: C{str}
531
532 @raise X2goControlSessionException: on SSH connection loss
533
534 """
535 if self._remote_username is None:
536 if self.get_transport() is not None:
537 try:
538 self._remote_username = self.get_transport().get_username()
539 except:
540 self.session_died = True
541 raise x2go_exceptions.X2goControlSessionException('Lost connection to X2Go server')
542 return self._remote_username
543
545 """\
546 Returns (and caches) the control session's remote host (name or ip).
547
548 @return: SSH transport's peer name
549 @rtype: C{tuple}
550
551 @raise X2goControlSessionException: on SSH connection loss
552
553 """
554 if self._remote_peername is None:
555 if self.get_transport() is not None:
556 try:
557 self._remote_peername = self.get_transport().getpeername()
558 except:
559 self.session_died = True
560 raise x2go_exceptions.X2goControlSessionException('Lost connection to X2Go server')
561 return self._remote_peername
562
563 @property
565 """\
566 Generate (and cache) a temporary RSA host key for the lifetime of this control session.
567
568 """
569 if self._session_auth_rsakey is None:
570 self._session_auth_rsakey = paramiko.RSAKey.generate(defaults.RSAKEY_STRENGTH)
571 return self._session_auth_rsakey
572
574 """\
575 Manipulate the control session's profile name.
576
577 @param profile_name: new profile name for this control session
578 @type profile_name: C{str}
579
580 """
581 self.profile_name = profile_name
582
584 """\
585 Wraps around a Paramiko/SSH host key check.
586
587 @param hostname: the remote X2Go server's hostname
588 @type hostname: C{str}
589 @param port: the SSH port of the remote X2Go server
590 @type port: C{int}
591
592 @return: C{True} if the host key check succeeded, C{False} otherwise
593 @rtype: C{bool}
594
595 """
596
597 hostname = hostname.strip()
598
599
600 if hostname in ('localhost', 'localhost.localdomain'):
601 hostname = '127.0.0.1'
602
603 return checkhosts.check_ssh_host_key(self, hostname, port=port)
604
605 - def connect(self, hostname, port=22, username='', password='', pkey=None,
606 use_sshproxy=False, sshproxy_host='', sshproxy_user='', sshproxy_password='',
607 sshproxy_key_filename='', sshproxy_tunnel='',
608 key_filename=None, timeout=None, allow_agent=False, look_for_keys=False,
609 session_instance=None,
610 add_to_known_hosts=False, force_password_auth=False):
611 """\
612 Connect to an X2Go server and authenticate to it. This method is directly
613 inherited from the C{paramiko.SSHClient} class. The features of the Paramiko
614 SSH client connect method are recited here. The parameters C{add_to_known_hosts},
615 C{force_password_auth}, C{session_instance} and all SSH proxy related parameters
616 have been added as X2Go specific parameters
617
618 The server's host key is checked against the system host keys
619 (see C{load_system_host_keys}) and any local host keys (C{load_host_keys}).
620 If the server's hostname is not found in either set of host keys, the missing host
621 key policy is used (see C{set_missing_host_key_policy}). The default policy is
622 to reject the key and raise an C{SSHException}.
623
624 Authentication is attempted in the following order of priority:
625
626 - The C{pkey} or C{key_filename} passed in (if any)
627 - Any key we can find through an SSH agent
628 - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/}
629 - Plain username/password auth, if a password was given
630
631 If a private key requires a password to unlock it, and a password is
632 passed in, that password will be used to attempt to unlock the key.
633
634 @param hostname: the server to connect to
635 @type hostname: C{str}
636 @param port: the server port to connect to
637 @type port: C{int}
638 @param username: the username to authenticate as (defaults to the
639 current local username)
640 @type username: C{str}
641 @param password: a password to use for authentication or for unlocking
642 a private key
643 @type password: C{str}
644 @param pkey: an optional private key to use for authentication
645 @type pkey: C{PKey}
646 @param key_filename: the filename, or list of filenames, of optional
647 private key(s) to try for authentication
648 @type key_filename: C{str} or list(str)
649 @param timeout: an optional timeout (in seconds) for the TCP connect
650 @type timeout: float
651 @param allow_agent: set to False to disable connecting to the SSH agent
652 @type allow_agent: C{bool}
653 @param look_for_keys: set to False to disable searching for discoverable
654 private key files in C{~/.ssh/}
655 @type look_for_keys: C{bool}
656 @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy()
657 is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy()
658 is used
659 @type add_to_known_hosts: C{bool}
660 @param force_password_auth: non-paramiko option, disable pub/priv key authentication
661 completely, even if the C{pkey} or the C{key_filename} parameter is given
662 @type force_password_auth: C{bool}
663 @param session_instance: an instance L{X2goSession} using this L{X2goControlSessionSTDOUT}
664 instance.
665 @type session_instance: C{obj}
666 @param use_sshproxy: connect through an SSH proxy
667 @type use_sshproxy: C{True} if an SSH proxy is to be used for tunneling the connection
668 @param sshproxy_host: hostname of the SSH proxy server, use <hostname>:<port> to name a
669 non-standard SSH port
670 @type sshproxy_host: C{str}
671 @param sshproxy_user: username that we use for authenticating against C{<sshproxy_host>}
672 @type sshproxy_user: C{str}
673 @param sshproxy_password: a password to use for SSH proxy authentication or for unlocking
674 a private key
675 @type sshproxy_password: C{str}
676 @param sshproxy_key_filename: local file location of the private key file
677 @type sshproxy_key_filename: C{str}
678 @param sshproxy_tunnel: the SSH proxy tunneling parameters, format is: <local-address>:<local-port>:<remote-address>:<remote-port>
679 @type sshproxy_tunnel: C{str}
680
681 @return: C{True} if an authenticated SSH transport could be retrieved by this method
682 @rtype: C{bool}
683
684 @raise BadHostKeyException: if the server's host key could not be
685 verified
686 @raise AuthenticationException: if authentication failed
687 @raise SSHException: if there was any other error connecting or
688 establishing an SSH session
689 @raise socket.error: if a socket error occurred while connecting
690 @raise X2goSSHProxyException: any SSH proxy exception is passed through while establishing the SSH proxy connection and tunneling setup
691 @raise X2goSSHAuthenticationException: any SSH proxy authentication exception is passed through while establishing the SSH proxy connection and tunneling setup
692 @raise X2goRemoteHomeException: if the remote home directory does not exist or is not accessible
693
694 """
695 if use_sshproxy and sshproxy_host and sshproxy_user:
696 try:
697 self.sshproxy_session = sshproxy.X2goSSHProxy(known_hosts=self.known_hosts,
698 sshproxy_host=sshproxy_host,
699 sshproxy_user=sshproxy_user,
700 sshproxy_password=sshproxy_password,
701 sshproxy_key_filename=sshproxy_key_filename,
702 sshproxy_tunnel=sshproxy_tunnel,
703 session_instance=session_instance,
704 logger=self.logger,
705 )
706
707 except:
708 if self.sshproxy_session:
709 self.sshproxy_session.stop_thread()
710 self.sshproxy_session = None
711 raise
712
713 if self.sshproxy_session is not None:
714 self.sshproxy_session.start()
715
716
717
718 gevent.sleep(.1)
719 port = self.sshproxy_session.get_local_proxy_port()
720
721 if not add_to_known_hosts and session_instance:
722 self.set_missing_host_key_policy(checkhosts.X2goInteractiveAddPolicy(caller=self, session_instance=session_instance))
723
724 if add_to_known_hosts:
725 self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
726
727
728 if force_password_auth:
729 key_filename = None
730 pkey = None
731
732
733 hostname = hostname.strip()
734
735 self.logger('connecting to [%s]:%s' % (hostname, port), loglevel=log.loglevel_NOTICE)
736
737 self.load_session_host_keys()
738
739 _hostname = hostname
740
741 if _hostname in ('localhost', 'localhost.localdomain'):
742 _hostname = '127.0.0.1'
743
744 if (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey:
745 try:
746 self.logger('trying SSH pub/priv key authentication with server', loglevel=log.loglevel_DEBUG)
747 if password:
748 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, pkey=pkey, password=password,
749 key_filename=key_filename, timeout=timeout, allow_agent=allow_agent,
750 look_for_keys=look_for_keys)
751 else:
752 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, pkey=pkey,
753 key_filename=key_filename, timeout=timeout, allow_agent=allow_agent,
754 look_for_keys=look_for_keys)
755
756
757 t = self.get_transport()
758 if hasattr(t, 'use_compression'):
759 t.use_compression(compress=True)
760
761 except paramiko.AuthenticationException, e:
762 self.close()
763 if password:
764 self.logger('next auth mechanism we\'ll try is keyboard-interactive authentication', loglevel=log.loglevel_DEBUG)
765 try:
766 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, password=password,
767 timeout=timeout, allow_agent=allow_agent,
768 look_for_keys=look_for_keys)
769 except paramiko.AuthenticationException, e:
770 self.close()
771 if self.sshproxy_session:
772 self.sshproxy_session.stop_thread()
773 raise e
774 except:
775 self.close()
776 if self.sshproxy_session:
777 self.sshproxy_session.stop_thread()
778 raise
779 else:
780 self.close()
781 if self.sshproxy_session:
782 self.sshproxy_session.stop_thread()
783 raise(e)
784
785 except:
786 self.close()
787 if self.sshproxy_session:
788 self.sshproxy_session.stop_thread()
789 raise
790
791
792 else:
793
794 if not password:
795 password = "".join([random.choice(string.letters+string.digits) for x in range(1, 20)])
796 self.logger('performing SSH keyboard-interactive authentication with server', loglevel=log.loglevel_DEBUG)
797 try:
798 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, password=password,
799 timeout=timeout, allow_agent=allow_agent, look_for_keys=look_for_keys)
800 except paramiko.AuthenticationException, e:
801 self.close()
802 if self.sshproxy_session:
803 self.sshproxy_session.stop_thread()
804 raise e
805 except:
806 self.close()
807 if self.sshproxy_session:
808 self.sshproxy_session.stop_thread()
809 raise
810
811 self.set_missing_host_key_policy(paramiko.RejectPolicy())
812
813 self.hostname = hostname
814 self.port = port
815
816
817 ssh_transport = self.get_transport()
818 ssh_transport.reverse_tunnels = {}
819
820
821 ssh_transport._x2go_session_marker = True
822 self._session_password = password
823
824 if self.get_transport():
825 self.session_died = False
826 self.query_server_features(force=True)
827
828 self._remote_home = None
829 if not self.home_exists():
830 raise x2go_exceptions.X2goRemoteHomeException('remote home directory does not exist')
831
832 return (self.get_transport() is not None)
833
835 """\
836 Drop an associated terminal session.
837
838 @param terminal_session: the terminal session object to remove from the list of associated terminals
839 @type terminal_session: C{X2goTerminalSession*}
840
841 """
842 for t_name in self.associated_terminals.keys():
843 if self.associated_terminals[t_name] == terminal_session:
844 del self.associated_terminals[t_name]
845 if self.terminated_terminals.has_key(t_name):
846 del self.terminated_terminals[t_name]
847
849 """\
850 Disconnect this control session from the remote server.
851
852 @return: report success or failure after having disconnected
853 @rtype: C{bool}
854
855 """
856 if self.associated_terminals:
857 t_names = self.associated_terminals.keys()
858 for t_obj in self.associated_terminals.values():
859 try:
860 if not self.session_died:
861 t_obj.suspend()
862 except x2go_exceptions.X2goTerminalSessionException:
863 pass
864 except x2go_exceptions.X2goControlSessionException:
865 self.session_died
866 t_obj.__del__()
867 for t_name in t_names:
868 try:
869 del self.associated_terminals[t_name]
870 except KeyError:
871 pass
872
873 self._remote_home = None
874 self._remote_group = {}
875
876 self._session_auth_rsakey = None
877
878
879 self._transport_lock.release()
880
881 retval = False
882 try:
883 if self.get_transport() is not None:
884 retval = self.get_transport().is_active()
885 try:
886 self.close()
887 except IOError:
888 pass
889 except AttributeError:
890
891
892 pass
893
894
895 if self.sshproxy_session is not None:
896 self.sshproxy_session.stop_thread()
897
898 return retval
899
901 """\
902 Test if the remote home directory exists.
903
904 @return: C{True} if the home directory exists, C{False} otherwise
905 @rtype: C{bool}
906
907 """
908 (_stdin, _stdout, _stderr) = self._x2go_exec_command('stat -tL "%s"' % self._x2go_remote_home, loglevel=log.loglevel_DEBUG)
909 if _stdout.read():
910 return True
911 return False
912
913
915 """\
916 Test if the connection to the remote X2Go server is still alive.
917
918 @return: C{True} if the connection is still alive, C{False} otherwise
919 @rtype: C{bool}
920
921 """
922 try:
923 if self._x2go_exec_command('echo', loglevel=log.loglevel_DEBUG):
924 return True
925 except x2go_exceptions.X2goControlSessionException:
926 self.session_died = True
927 return False
928
930 """\
931 Test if the connection to the remote X2Go server died on the way.
932
933 @return: C{True} if the connection has died, C{False} otherwise
934 @rtype: C{bool}
935
936 """
937 return self.session_died
938
940 """\
941 Retrieve the menu tree of published applications from the remote X2Go server.
942
943 The C{raw} option lets this method return a C{list} of C{dict} elements. Each C{dict} elements has a
944 C{desktop} key containing a shortened version of the text output of a .desktop file and an C{icon} key
945 which contains the desktop base64-encoded icon data.
946
947 The {very_raw} lets this method return the output of the C{x2gogetapps} script as is.
948
949 @param lang: locale/language identifier
950 @type lang: C{str}
951 @param refresh: force reload of the menu tree from X2Go server
952 @type refresh: C{bool}
953 @param raw: retrieve a raw output of the server list of published applications
954 @type raw: C{bool}
955 @param very_raw: retrieve a very raw output of the server list of published applications
956 @type very_raw: C{bool}
957
958 @return: an i18n capable menu tree packed as a Python dictionary
959 @rtype: C{list}
960
961 """
962
963 if self._already_querying_published_applications:
964 self.logger('This control session instance is currently already querying the published applications menu tree for session profile %s. Whenever this warning pops up in your log file it means that you should fix your client implementation code. Only call this method once per session profile!!!' % self.profile_name, loglevel=log.loglevel_WARN)
965 return None
966 else:
967 self._already_querying_published_applications = True
968
969 if defaults.X2GOCLIENT_OS != 'Windows' and lang is None:
970 lang = locale.getdefaultlocale()[0]
971 elif lang is None:
972 lang = 'en'
973
974 if 'X2GO_PUBLISHED_APPLICATIONS' in self.get_server_features():
975 if self._published_applications_menu is {} or \
976 not self._published_applications_menu.has_key(lang) or \
977 raw or very_raw or refresh or \
978 (self.published_applications_no_submenus != max_no_submenus):
979
980 self.published_applications_no_submenus = max_no_submenus
981
982
983
984 self.logger('querying server (%s) for list of published applications' % self.profile_name, loglevel=log.loglevel_NOTICE)
985 (stdin, stdout, stderr) = self._x2go_exec_command('which x2gogetapps >/dev/null && x2gogetapps')
986 _raw_output = stdout.read()
987
988 if very_raw:
989 self.logger('published applications query for %s finished, return very raw output' % self.profile_name, loglevel=log.loglevel_NOTICE)
990 return _raw_output
991
992
993
994 _raw_menu_items = _raw_output.split('</desktop>\n')
995 _raw_menu_items = [ i.replace('<desktop>\n', '') for i in _raw_menu_items ]
996 _menu = []
997 for _raw_menu_item in _raw_menu_items:
998 if '<icon>\n' in _raw_menu_item and '</icon>' in _raw_menu_item:
999 _menu_item = _raw_menu_item.split('<icon>\n')[0] + _raw_menu_item.split('</icon>\n')[1]
1000 _icon_base64 = _raw_menu_item.split('<icon>\n')[1].split('</icon>\n')[0]
1001 else:
1002 _menu_item = _raw_menu_item
1003 _icon_base64 = None
1004 if _menu_item:
1005 _menu.append({ 'desktop': _menu_item, 'icon': _icon_base64, })
1006 _menu_item = None
1007 _icon_base64 = None
1008
1009 if raw:
1010 self.logger('published applications query for %s finished, returning raw output' % self.profile_name, loglevel=log.loglevel_NOTICE)
1011 return _menu
1012
1013 if len(_menu) > max_no_submenus >= 0:
1014 _render_submenus = True
1015 else:
1016 _render_submenus = False
1017
1018
1019
1020 _category_map = {
1021 lang: {
1022 'Multimedia': [],
1023 'Development': [],
1024 'Education': [],
1025 'Games': [],
1026 'Graphics': [],
1027 'Internet': [],
1028 'Office': [],
1029 'System': [],
1030 'Utilities': [],
1031 'Other Applications': [],
1032 'TOP': [],
1033 }
1034 }
1035 _empty_menus = _category_map[lang].keys()
1036
1037 for item in _menu:
1038
1039 _menu_entry_name = ''
1040 _menu_entry_fallback_name = ''
1041 _menu_entry_comment = ''
1042 _menu_entry_fallback_comment = ''
1043 _menu_entry_exec = ''
1044 _menu_entry_cat = ''
1045 _menu_entry_shell = False
1046
1047 lang_regio = lang
1048 lang_only = lang_regio.split('_')[0]
1049
1050 for line in item['desktop'].split('\n'):
1051 if re.match('^Name\[%s\]=.*' % lang_regio, line) or re.match('Name\[%s\]=.*' % lang_only, line):
1052 _menu_entry_name = line.split("=")[1].strip()
1053 elif re.match('^Name=.*', line):
1054 _menu_entry_fallback_name = line.split("=")[1].strip()
1055 elif re.match('^Comment\[%s\]=.*' % lang_regio, line) or re.match('Comment\[%s\]=.*' % lang_only, line):
1056 _menu_entry_comment = line.split("=")[1].strip()
1057 elif re.match('^Comment=.*', line):
1058 _menu_entry_fallback_comment = line.split("=")[1].strip()
1059 elif re.match('^Exec=.*', line):
1060 _menu_entry_exec = line.split("=")[1].strip()
1061 elif re.match('^Terminal=.*(t|T)(r|R)(u|U)(e|E).*', line):
1062 _menu_entry_shell = True
1063 elif re.match('^Categories=.*', line):
1064 if 'X2Go-Top' in line:
1065 _menu_entry_cat = 'TOP'
1066 elif 'Audio' in line or 'Video' in line:
1067 _menu_entry_cat = 'Multimedia'
1068 elif 'Development' in line:
1069 _menu_entry_cat = 'Development'
1070 elif 'Education' in line:
1071 _menu_entry_cat = 'Education'
1072 elif 'Game' in line:
1073 _menu_entry_cat = 'Games'
1074 elif 'Graphics' in line:
1075 _menu_entry_cat = 'Graphics'
1076 elif 'Network' in line:
1077 _menu_entry_cat = 'Internet'
1078 elif 'Office' in line:
1079 _menu_entry_cat = 'Office'
1080 elif 'Settings' in line:
1081 continue
1082 elif 'System' in line:
1083 _menu_entry_cat = 'System'
1084 elif 'Utilities' in line:
1085 _menu_entry_cat = 'Utilities'
1086 else:
1087 _menu_entry_cat = 'Other Applications'
1088
1089 if not _menu_entry_exec:
1090 continue
1091 else:
1092
1093 _menu_entry_exec = _menu_entry_exec.replace('%f', '').replace('%F','').replace('%u','').replace('%U','')
1094 if _menu_entry_shell:
1095 _menu_entry_exec = "x-terminal-emulator -e '%s'" % _menu_entry_exec
1096
1097 if not _menu_entry_cat:
1098 _menu_entry_cat = 'Other Applications'
1099
1100 if not _render_submenus:
1101 _menu_entry_cat = 'TOP'
1102
1103 if _menu_entry_cat in _empty_menus:
1104 _empty_menus.remove(_menu_entry_cat)
1105
1106 if not _menu_entry_name: _menu_entry_name = _menu_entry_fallback_name
1107 if not _menu_entry_comment: _menu_entry_comment = _menu_entry_fallback_comment
1108 if not _menu_entry_comment: _menu_entry_comment = _menu_entry_name
1109
1110 _menu_entry_icon = item['icon']
1111
1112 _category_map[lang][_menu_entry_cat].append(
1113 {
1114 'name': _menu_entry_name,
1115 'comment': _menu_entry_comment,
1116 'exec': _menu_entry_exec,
1117 'icon': _menu_entry_icon,
1118 }
1119 )
1120
1121 for _cat in _empty_menus:
1122 del _category_map[lang][_cat]
1123
1124 for _cat in _category_map[lang].keys():
1125 _sorted = sorted(_category_map[lang][_cat], key=lambda k: k['name'])
1126 _category_map[lang][_cat] = _sorted
1127
1128 self._published_applications_menu.update(_category_map)
1129 self.logger('published applications query for %s finished, return menu tree' % self.profile_name, loglevel=log.loglevel_NOTICE)
1130
1131 else:
1132
1133 pass
1134
1135 self._already_querying_published_applications = False
1136 return self._published_applications_menu
1137
1138
1139 - def start(self, **kwargs):
1140 """\
1141 Start a new X2Go session.
1142
1143 The L{X2goControlSessionSTDOUT.start()} method accepts any parameter
1144 that can be passed to any of the C{X2goTerminalSession} backend class
1145 constructors.
1146
1147 @param kwargs: parameters that get passed through to the control session's
1148 L{resume()} method, only the C{session_name} parameter will get removed
1149 before pass-through
1150 @type kwargs: C{dict}
1151
1152 @return: return value of the cascaded L{resume()} method, denoting the success or failure
1153 of the session startup
1154 @rtype: C{bool}
1155
1156 """
1157 if 'session_name' in kwargs.keys():
1158 del kwargs['session_name']
1159 return self.resume(**kwargs)
1160
1161 - def resume(self, session_name=None, session_instance=None, session_list=None, **kwargs):
1162 """\
1163 Resume a running/suspended X2Go session.
1164
1165 The L{X2goControlSessionSTDOUT.resume()} method accepts any parameter
1166 that can be passed to any of the C{X2goTerminalSession*} backend class constructors.
1167
1168 @return: True if the session could be successfully resumed
1169 @rtype: C{bool}
1170
1171 @raise X2goUserException: if the remote user is not allowed to launch/resume X2Go sessions.
1172
1173 """
1174 if not self.is_x2gouser(self.get_transport().get_username()):
1175 raise x2go_exceptions.X2goUserException('remote user %s is not allowed to run X2Go commands' % self.get_transport().get_username())
1176
1177 if session_name is not None:
1178 if session_list:
1179 session_info = session_list[session_name]
1180 else:
1181 session_info = self.list_sessions()[session_name]
1182 else:
1183 session_info = None
1184
1185 _terminal = self._terminal_backend(self,
1186 profile_name=self.profile_name,
1187 session_info=session_info,
1188 info_backend=self._info_backend,
1189 list_backend=self._list_backend,
1190 proxy_backend=self._proxy_backend,
1191 client_rootdir=self.client_rootdir,
1192 session_instance=session_instance,
1193 sessions_rootdir=self.sessions_rootdir,
1194 **kwargs)
1195
1196 _success = False
1197 try:
1198 if session_name is not None:
1199 _success = _terminal.resume()
1200 else:
1201 _success = _terminal.start()
1202 except x2go_exceptions.X2goTerminalSessionException:
1203 _success = False
1204
1205 if _success:
1206 while not _terminal.ok():
1207 gevent.sleep(.2)
1208
1209 if _terminal.ok():
1210 self.associated_terminals[_terminal.get_session_name()] = _terminal
1211 self.get_transport().reverse_tunnels[_terminal.get_session_name()] = {
1212 'sshfs': (0, None),
1213 'snd': (0, None),
1214 }
1215
1216 return _terminal or None
1217
1218 return None
1219
1220 - def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, **kwargs):
1221 """\
1222 Share another already running desktop session. Desktop sharing can be run
1223 in two different modes: view-only and full-access mode.
1224
1225 @param desktop: desktop ID of a sharable desktop in format C{<user>@<display>}
1226 @type desktop: C{str}
1227 @param user: user name and display number can be given separately, here give the
1228 name of the user who wants to share a session with you
1229 @type user: C{str}
1230 @param display: user name and display number can be given separately, here give the
1231 number of the display that a user allows you to be shared with
1232 @type display: C{str}
1233 @param share_mode: desktop sharing mode, 0 stands for VIEW-ONLY, 1 for FULL-ACCESS mode
1234 @type share_mode: C{int}
1235
1236 @return: True if the session could be successfully shared
1237 @rtype: C{bool}
1238
1239 @raise X2goDesktopSharingException: if C{username} and C{dislpay} do not relate to a
1240 sharable desktop session
1241
1242 """
1243 if desktop:
1244 user = desktop.split('@')[0]
1245 display = desktop.split('@')[1]
1246 if not (user and display):
1247 raise x2go_exceptions.X2goDesktopSharingException('Need user name and display number of sharable desktop.')
1248
1249 cmd = '%sXSHAD%sXSHAD%s' % (share_mode, user, display)
1250
1251 kwargs['cmd'] = cmd
1252 kwargs['session_type'] = 'shared'
1253
1254 return self.start(**kwargs)
1255
1257 """\
1258 List all desktop-like sessions of current user (or of users that have
1259 granted desktop sharing) on the connected server.
1260
1261 @param raw: if C{True}, the raw output of the server-side X2Go command
1262 C{x2golistdesktops} is returned.
1263 @type raw: C{bool}
1264
1265 @return: a list of X2Go desktops available for sharing
1266 @rtype: C{list}
1267
1268 @raise X2goTimeOutException: on command execution timeouts, with the server-side C{x2golistdesktops}
1269 command this can sometimes happen. Make sure you ignore these time-outs and to try again
1270
1271 """
1272 if raw:
1273 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops")
1274 return stdout.read(), stderr.read()
1275
1276 else:
1277
1278
1279
1280
1281 timeout = gevent.Timeout(maxwait)
1282 timeout.start()
1283 try:
1284 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops")
1285 _stdout_read = stdout.read()
1286 _listdesktops = _stdout_read.split('\n')
1287 except gevent.timeout.Timeout:
1288
1289
1290
1291 raise x2go_exceptions.X2goTimeOutException('x2golistdesktop command timed out')
1292 finally:
1293 timeout.cancel()
1294
1295 return _listdesktops
1296
1297 - def list_mounts(self, session_name, raw=False, maxwait=20):
1298 """\
1299 List all mounts for a given session of the current user on the connected server.
1300
1301 @param session_name: name of a session to query a list of mounts for
1302 @type session_name: C{str}
1303 @param raw: if C{True}, the raw output of the server-side X2Go command
1304 C{x2golistmounts} is returned.
1305 @type raw: C{bool}
1306 @param maxwait: stop processing C{x2golistmounts} after C{<maxwait>} seconds
1307 @type maxwait: C{int}
1308
1309 @return: a list of client-side mounts for X2Go session C{<session_name>} on the server
1310 @rtype: C{list}
1311
1312 @raise X2goTimeOutException: on command execution timeouts, queries with the server-side
1313 C{x2golistmounts} query should normally be processed quickly, a time-out may hint that the
1314 control session has lost its connection to the X2Go server
1315
1316 """
1317 if raw:
1318 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistmounts %s" % session_name)
1319 return stdout.read(), stderr.read()
1320
1321 else:
1322
1323
1324
1325 timeout = gevent.Timeout(maxwait)
1326 timeout.start()
1327 try:
1328 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistmounts %s" % session_name)
1329 _stdout_read = stdout.read()
1330 _listmounts = {session_name: [ line for line in _stdout_read.split('\n') if line ] }
1331 except gevent.timeout.Timeout:
1332
1333
1334 raise x2go_exceptions.X2goTimeOutException('x2golistmounts command timed out')
1335 finally:
1336 timeout.cancel()
1337
1338 return _listmounts
1339
1341 """\
1342 List all sessions of current user on the connected server.
1343
1344 @param raw: if C{True}, the raw output of the server-side X2Go command
1345 C{x2golistsessions} is returned.
1346 @type raw: C{bool}
1347
1348 @return: normally an instance of a C{X2goServerSessionList*} backend is returned. However,
1349 if the raw argument is set, the plain text output of the server-side C{x2golistsessions}
1350 command is returned
1351 @rtype: C{X2goServerSessionList} instance or str
1352
1353 @raise X2goControlSessionException: on command execution timeouts, if this happens the control session will
1354 be interpreted as disconnected due to connection loss
1355 """
1356 if raw:
1357 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
1358 return stdout.read(), stderr.read()
1359
1360 else:
1361
1362
1363
1364 _listsessions = {}
1365 _success = False
1366 _count = 0
1367 _maxwait = 20
1368
1369
1370
1371 while not _success and _count < _maxwait:
1372 _count += 1
1373 try:
1374 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
1375 _stdout_read = stdout.read()
1376 _listsessions = self._list_backend(_stdout_read, info_backend=self._info_backend).sessions
1377 _success = True
1378 except KeyError:
1379 gevent.sleep(1)
1380 except IndexError:
1381 gevent.sleep(1)
1382 except ValueError:
1383 gevent.sleep(1)
1384
1385 if _count >= _maxwait:
1386 self.session_died = True
1387 raise x2go_exceptions.X2goControlSessionException('x2golistsessions command failed after we have tried 20 times')
1388
1389
1390 for _session_name, _terminal in self.associated_terminals.items():
1391 if _session_name in _listsessions.keys():
1392
1393 if hasattr(self.associated_terminals[_session_name], 'session_info') and not self.associated_terminals[_session_name].is_session_info_protected():
1394 self.associated_terminals[_session_name].session_info.update(_listsessions[_session_name])
1395 else:
1396 try: del self.associated_terminals[_session_name]
1397 except KeyError: pass
1398 self.terminated_terminals.append(_session_name)
1399 if _terminal.is_suspended():
1400 try: del self.associated_terminals[_session_name]
1401 except KeyError: pass
1402
1403
1404 return _listsessions
1405
1406 - def clean_sessions(self, destroy_terminals=True, published_applications=False):
1407 """\
1408 Find X2Go terminals that have previously been started by the
1409 connected user on the remote X2Go server and terminate them.
1410
1411 @param destroy_terminals: destroy the terminal session instances after cleanup
1412 @type destroy_terminals: C{bool}
1413 @param published_applications: also clean up published applications providing sessions
1414 @type published_applications: C{bool}
1415
1416 """
1417 session_list = self.list_sessions()
1418 if published_applications:
1419 session_names = session_list.keys()
1420 else:
1421 session_names = [ _sn for _sn in session_list.keys() if not session_list[_sn].is_published_applications_provider() ]
1422 for session_name in session_names:
1423 self.terminate(session_name=session_name, destroy_terminals=destroy_terminals)
1424
1426 """\
1427 Returns C{True} if this control session is connected to the remote server (that
1428 is: if it has a valid Paramiko/SSH transport object).
1429
1430 @return: X2Go session connected?
1431 @rtype: C{bool}
1432
1433 """
1434 return self.get_transport() is not None and self.get_transport().is_authenticated()
1435
1437 """\
1438 Returns C{True} if the given X2Go session is in running state,
1439 C{False} else.
1440
1441 @param session_name: X2Go name of the session to be queried
1442 @type session_name: C{str}
1443
1444 @return: X2Go session running? If C{<session_name>} is not listable by the L{list_sessions()} method then C{None} is returned
1445 @rtype: C{bool} or C{None}
1446
1447 """
1448 session_infos = self.list_sessions()
1449 if session_name in session_infos.keys():
1450 return session_infos[session_name].is_running()
1451 return None
1452
1454 """\
1455 Returns C{True} if the given X2Go session is in suspended state,
1456 C{False} else.
1457
1458 @return: X2Go session suspended? If C{<session_name>} is not listable by the L{list_sessions()} method then C{None} is returned
1459 @rtype: C{bool} or C{None}
1460
1461 """
1462 session_infos = self.list_sessions()
1463 if session_name in session_infos.keys():
1464 return session_infos[session_name].is_suspended()
1465 return None
1466
1468 """\
1469 Returns C{True} if the X2Go session with name C{<session_name>} has been seen
1470 by this control session and--in the meantime--has been terminated.
1471
1472 If C{<session_name>} has not been seen, yet, the method will return C{None}.
1473
1474 @return: X2Go session has terminated?
1475 @rtype: C{bool} or C{None}
1476
1477 """
1478 session_infos = self.list_sessions()
1479 if session_name in self.terminated_terminals:
1480 return True
1481 if session_name not in session_infos.keys() and session_name in self.associated_terminals.keys():
1482
1483 self.terminate(session_name)
1484 return True
1485 if self.is_suspended(session_name) or self.is_running(session_name):
1486 return False
1487
1488 return None
1489
1491 """\
1492 Suspend X2Go session with name C{<session_name>} on the connected
1493 server.
1494
1495 @param session_name: X2Go name of the session to be suspended
1496 @type session_name: C{str}
1497
1498 @return: C{True} if the session could be successfully suspended
1499 @rtype: C{bool}
1500
1501 """
1502 _ret = False
1503 _session_names = [ t.get_session_name() for t in self.associated_terminals.values() ]
1504 if session_name in _session_names:
1505
1506 self.logger('suspending associated terminal session: %s' % session_name, loglevel=log.loglevel_DEBUG)
1507 (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG)
1508 stdout.read()
1509 stderr.read()
1510 if self.associated_terminals.has_key(session_name):
1511 if self.associated_terminals[session_name] is not None:
1512 self.associated_terminals[session_name].__del__()
1513 try: del self.associated_terminals[session_name]
1514 except KeyError: pass
1515 _ret = True
1516
1517 else:
1518
1519 self.logger('suspending non-associated terminal session: %s' % session_name, loglevel=log.loglevel_DEBUG)
1520 (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG)
1521 stdout.read()
1522 stderr.read()
1523 _ret = True
1524
1525 return _ret
1526
1527 - def terminate(self, session_name, destroy_terminals=True):
1528 """\
1529 Terminate X2Go session with name C{<session_name>} on the connected
1530 server.
1531
1532 @param session_name: X2Go name of the session to be terminated
1533 @type session_name: C{str}
1534
1535 @return: C{True} if the session could be successfully terminated
1536 @rtype: C{bool}
1537
1538 """
1539
1540 _ret = False
1541 _session_names = [ t.get_session_name() for t in self.associated_terminals.values() ]
1542 if session_name in _session_names:
1543
1544 self.logger('terminating associated session: %s' % session_name, loglevel=log.loglevel_DEBUG)
1545 (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG)
1546 stdout.read()
1547 stderr.read()
1548 if self.associated_terminals.has_key(session_name):
1549 if self.associated_terminals[session_name] is not None and destroy_terminals:
1550 self.associated_terminals[session_name].__del__()
1551 try: del self.associated_terminals[session_name]
1552 except KeyError: pass
1553 self.terminated_terminals.append(session_name)
1554 _ret = True
1555
1556 else:
1557
1558 self.logger('terminating non-associated session: %s' % session_name, loglevel=log.loglevel_DEBUG)
1559 (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG)
1560 stdout.read()
1561 stderr.read()
1562 _ret = True
1563
1564 return _ret
1565