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
34 import copy
35 import binascii
36
37 import string
38 import random
39
40 from gevent import socket
41
42
43 import x2go.sshproxy as sshproxy
44 import x2go.log as log
45 import x2go.utils as utils
46 import x2go.x2go_exceptions as x2go_exceptions
47 import x2go.defaults as defaults
48 import x2go.checkhosts as checkhosts
49
50 from x2go.backends.terminal import X2goTerminalSession as _X2goTerminalSession
51 from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo
52 from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList
53 from x2go.backends.proxy import X2goProxy as _X2goProxy
54
55 from x2go.monkey_patch_paramiko import monkey_patch_paramiko
56 monkey_patch_paramiko()
59
60 if cmd:
61 cmd = cmd.replace("X2GO_SPACE_CHAR", " ")
62 return cmd
63
65
66
67
68 if cmd and user:
69 cmd = cmd.replace('X2GO_USER', user)
70
71
72 if cmd and password:
73 cmd = cmd.replace('X2GO_PASSWORD', password)
74 return cmd
75
78 """\
79 STILL UNDOCUMENTED
80
81 @param logger: you can pass an L{X2goLogger} object to the
82 L{X2goControlSessionSTDOUT} constructor
83 @type logger: L{X2goLogger} instance
84 @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
85 constructed with the given loglevel
86 @type loglevel: int
87
88 """
89 associated_terminals = None
90
91 - def __init__(self,
92 profile_name='UNKNOWN',
93 add_to_known_hosts=False,
94 known_hosts=None,
95 terminal_backend=_X2goTerminalSession,
96 info_backend=_X2goServerSessionInfo,
97 list_backend=_X2goServerSessionList,
98 proxy_backend=_X2goProxy,
99 client_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_CLIENT_ROOTDIR),
100 sessions_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_SESSIONS_ROOTDIR),
101 ssh_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_SSH_ROOTDIR),
102 logger=None, loglevel=log.loglevel_DEFAULT,
103 *args, **kwargs):
104 """\
105 Initialize an X2go session. With the L{X2goControlSessionSTDOUT} class you can start
106 new X2go sessions, resume suspended sessions or suspend resp. terminate
107 currently running sessions on a connected X2go server.
108
109 """
110 self.associated_terminals = {}
111 self.terminated_terminals = []
112
113 self.profile_name = profile_name
114 self.add_to_known_hosts = add_to_known_hosts
115 self.known_hosts = known_hosts
116
117 self.hostname = None
118 self.port = None
119
120 self.sshproxy_session = None
121
122 self._session_auth_rsakey = None
123 self._remote_home = None
124 self._remote_group = {}
125
126 self.locked = False
127
128 if logger is None:
129 self.logger = log.X2goLogger(loglevel=loglevel)
130 else:
131 self.logger = copy.deepcopy(logger)
132 self.logger.tag = __NAME__
133
134 self._terminal_backend = terminal_backend
135 self._info_backend = info_backend
136 self._list_backend = list_backend
137 self._proxy_backend = proxy_backend
138
139 self.client_rootdir = client_rootdir
140 self.sessions_rootdir = sessions_rootdir
141 self.ssh_rootdir = ssh_rootdir
142
143 paramiko.SSHClient.__init__(self, *args, **kwargs)
144 if self.add_to_known_hosts:
145 self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
146
147 self.session_died = False
148
150 if self.known_hosts is not None:
151 utils.touch_file(self.known_hosts)
152 self.load_host_keys(self.known_hosts)
153
157
159
160 self.logger('sFTP-put: %s -> %s:%s' % (os.path.normpath(local_path), self.get_transport().getpeername(), remote_path), loglevel=log.loglevel_DEBUG)
161 self.sftp_client.put(os.path.normpath(local_path), remote_path)
162
164
165 self.logger('sFTP-write: opening remote file %s on host %s for writing' % (remote_path, self.get_transport().getpeername()), loglevel=log.loglevel_DEBUG)
166 try:
167 remote_fileobj = self.sftp_client.open(remote_path, 'w')
168 self.logger('sFTP-write: writing content: %s' % content, loglevel=log.loglevel_DEBUG_SFTPXFER)
169 remote_fileobj.write(content)
170 remote_fileobj.close()
171 except SSHException:
172 self.logger('sFTP-write: opening remote file %s on host %s failed' % (remote_path, self.get_transport().getpeername()), loglevel=log.loglevel_WARN)
173
175
176 self.logger('sFTP-write: removing remote file %s on host %s' % (remote_path, self.get_transport().getpeername()), loglevel=log.loglevel_DEBUG)
177 self.sftp_client.remove(remote_path)
178
180
181 while self.locked:
182 gevent.sleep(.1)
183
184 self.locked = True
185 _retval = None
186
187 if type(cmd_line) == types.ListType:
188 cmd = " ".join(cmd_line)
189 else:
190 cmd = cmd_line
191 if self.get_transport() is not None:
192
193 timeout = gevent.Timeout(20)
194 timeout.start()
195 try:
196 self.logger("executing command on X2go server ,,%s'': %s" % (self.profile_name, _rerewrite_blanks(cmd)), loglevel)
197 _retval = self.exec_command(_rewrite_password(cmd, user=self.get_transport().get_username(), password=self._session_password), **kwargs)
198 except AttributeError:
199 self.session_died = True
200 if self.sshproxy_session:
201 self.sshproxy_session.stop_thread()
202 self.locked = False
203 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly')
204 except EOFError:
205 self.session_died = True
206 if self.sshproxy_session:
207 self.sshproxy_session.stop_thread()
208 self.locked = False
209 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly')
210 except x2go_exceptions.SSHException:
211 self.session_died = True
212 if self.sshproxy_session:
213 self.sshproxy_session.stop_thread()
214 self.locked = False
215 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly')
216 except gevent.timeout.Timeout:
217 self.session_died = True
218 if self.sshproxy_session:
219 self.sshproxy_session.stop_thread()
220 self.locked = False
221 raise x2go_exceptions.X2goControlSessionException('the X2go control session command timed out')
222 except socket.error:
223 self.session_died = True
224 if self.sshproxy_session:
225 self.sshproxy_session.stop_thread()
226 self.locked = False
227 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly')
228 finally:
229 self.locked = False
230 timeout.cancel()
231
232 else:
233 self.locked = False
234 raise x2go_exceptions.X2goControlSessionException('the X2go control session is not connected')
235 return _retval
236
237 @property
239
240 if self._remote_home is None:
241 (stdin, stdout, stderr) = self._x2go_exec_command('echo $HOME')
242 self._remote_home = stdout.read().split()[0]
243 self.logger('remote user\' home directory: %s' % self._remote_home, loglevel=log.loglevel_DEBUG)
244 return self._remote_home
245 else:
246 return self._remote_home
247
249
250 if not self._remote_group.has_key(group):
251 (stdin, stdout, stderr) = self._x2go_exec_command('getent group %s | cut -d":" -f4' % group)
252 self._remote_group[group] = stdout.read().split('\n')[0].split(',')
253 self.logger('remote %s group: %s' % (group, self._remote_group[group]), loglevel=log.loglevel_DEBUG)
254 return self._remote_group[group]
255 else:
256 return self._remote_group[group]
257
259
260
261
262
263
264
265 return True
266
268 if self.remote_username() in self._x2go_remote_group('fuse'):
269 return True
270 return False
271
273 """\
274 Returns the control session's remote username.
275
276 """
277 if self.get_transport() is not None:
278 return self.get_transport().get_username()
279 else:
280 return None
281
282 @property
284 if self._session_auth_rsakey is None:
285 self._session_auth_rsakey = paramiko.RSAKey.generate(defaults.RSAKEY_STRENGTH)
286 return self._session_auth_rsakey
287
289 self.profile_name = profile_name
290
292 """\
293 Wraps around a Paramiko/SSH host key check.
294
295 """
296
297 hostname = hostname.strip()
298
299
300 if hostname in ('localhost', 'localhost.localdomain'):
301 hostname = '127.0.0.1'
302
303 return checkhosts.check_ssh_host_key(self, hostname, port=port)
304
305 - def connect(self, hostname, port=22, username='', password='', pkey=None,
306 use_sshproxy=False, sshproxy_host='', sshproxy_user='', sshproxy_password='',
307 sshproxy_key_filename='', sshproxy_tunnel='',
308 key_filename=None, timeout=None, allow_agent=False, look_for_keys=False,
309 session_instance=None,
310 add_to_known_hosts=False, force_password_auth=False):
311 """\
312 Connect to an X2go server and authenticate to it. This method is directly
313 inherited from the paramiko.SSHClient module. The features of the Paramiko
314 SSH client connect method are recited here. The parameters C{add_to_known_hosts}
315 and C{force_password_auth} have been added as a parameter for X2go.
316
317 The server's host key
318 is checked against the system host keys (see C{load_system_host_keys})
319 and any local host keys (C{load_host_keys}). If the server's hostname
320 is not found in either set of host keys, the missing host key policy
321 is used (see C{set_missing_host_key_policy}). The default policy is
322 to reject the key and raise an C{SSHException}.
323
324 Authentication is attempted in the following order of priority:
325
326 - The C{pkey} or C{key_filename} passed in (if any)
327 - Any key we can find through an SSH agent
328 - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/}
329 - Plain username/password auth, if a password was given
330
331 If a private key requires a password to unlock it, and a password is
332 passed in, that password will be used to attempt to unlock the key.
333
334 @param hostname: the server to connect to
335 @type hostname: str
336 @param port: the server port to connect to
337 @type port: int
338 @param username: the username to authenticate as (defaults to the
339 current local username)
340 @type username: str
341 @param password: a password to use for authentication or for unlocking
342 a private key
343 @type password: str
344 @param pkey: an optional private key to use for authentication
345 @type pkey: C{PKey}
346 @param key_filename: the filename, or list of filenames, of optional
347 private key(s) to try for authentication
348 @type key_filename: str or list(str)
349 @param timeout: an optional timeout (in seconds) for the TCP connect
350 @type timeout: float
351 @param allow_agent: set to False to disable connecting to the SSH agent
352 @type allow_agent: C{bool}
353 @param look_for_keys: set to False to disable searching for discoverable
354 private key files in C{~/.ssh/}
355 @type look_for_keys: C{bool}
356 @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy()
357 is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy()
358 is used
359 @type add_to_known_hosts: C{bool}
360 @param force_password_auth: non-paramiko option, disable pub/priv key authentication
361 completely, even if the C{pkey} or the C{key_filename} parameter is given
362 @type force_password_auth: C{bool}
363 @param session_instance: an instance L{X2goSession} using this L{X2goControlSessionSTDOUT}
364 instance.
365 @type session_instance: C{instance}
366
367 @raise BadHostKeyException: if the server's host key could not be
368 verified
369 @raise AuthenticationException: if authentication failed
370 @raise SSHException: if there was any other error connecting or
371 establishing an SSH session
372 @raise socket.error: if a socket error occurred while connecting
373
374 """
375 if use_sshproxy and sshproxy_host and sshproxy_user:
376 try:
377 self.sshproxy_session = sshproxy.X2goSSHProxy(known_hosts=self.known_hosts,
378 sshproxy_host=sshproxy_host,
379 sshproxy_user=sshproxy_user,
380 sshproxy_password=sshproxy_password,
381 sshproxy_key_filename=sshproxy_key_filename,
382 sshproxy_tunnel=sshproxy_tunnel,
383 session_instance=session_instance,
384 logger=self.logger,
385 )
386
387 except:
388 if self.sshproxy_session:
389 self.sshproxy_session.stop_thread()
390 self.sshproxy_session = None
391 raise
392
393 if self.sshproxy_session is not None:
394 self.sshproxy_session.start()
395
396
397
398 gevent.sleep(.1)
399 port = self.sshproxy_session.get_local_proxy_port()
400
401 if not add_to_known_hosts and session_instance:
402 self.set_missing_host_key_policy(checkhosts.X2goInteractiveAddPolicy(caller=self, session_instance=session_instance))
403
404 if add_to_known_hosts:
405 self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
406
407
408 if force_password_auth:
409 key_filename = None
410 pkey = None
411
412
413 hostname = hostname.strip()
414
415 self.logger('connecting to [%s]:%s' % (hostname, port), loglevel=log.loglevel_NOTICE)
416
417 self.load_session_host_keys()
418
419 _hostname = hostname
420
421 if _hostname in ('localhost', 'localhost.localdomain'):
422 _hostname = '127.0.0.1'
423
424 if (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey:
425 try:
426 self.logger('trying SSH pub/priv key authentication with server', loglevel=log.loglevel_DEBUG)
427 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, pkey=pkey,
428 key_filename=key_filename, timeout=timeout, allow_agent=allow_agent,
429 look_for_keys=look_for_keys)
430
431 except paramiko.AuthenticationException, e:
432 self.close()
433 if password:
434 self.logger('next auth mechanism we\'ll try is keyboard-interactive authentication', loglevel=log.loglevel_DEBUG)
435 try:
436 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, password=password,
437 timeout=timeout, allow_agent=allow_agent,
438 look_for_keys=look_for_keys)
439 except paramiko.AuthenticationException, e:
440 self.close()
441 if self.sshproxy_session:
442 self.sshproxy_session.stop_thread()
443 raise e
444 except:
445 self.close()
446 if self.sshproxy_session:
447 self.sshproxy_session.stop_thread()
448 raise
449 else:
450 self.close()
451 if self.sshproxy_session:
452 self.sshproxy_session.stop_thread()
453 raise(e)
454
455 except:
456 self.close()
457 if self.sshproxy_session:
458 self.sshproxy_session.stop_thread()
459 raise
460
461
462 else:
463
464 if not password:
465 password = "".join([random.choice(string.letters+string.digits) for x in range(1, 20)])
466 self.logger('performing SSH keyboard-interactive authentication with server', loglevel=log.loglevel_DEBUG)
467 try:
468 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, password=password,
469 timeout=timeout, allow_agent=allow_agent, look_for_keys=look_for_keys)
470 except paramiko.AuthenticationException, e:
471 self.close()
472 if self.sshproxy_session:
473 self.sshproxy_session.stop_thread()
474 raise e
475 except:
476 self.close()
477 if self.sshproxy_session:
478 self.sshproxy_session.stop_thread()
479 raise
480
481 self.set_missing_host_key_policy(paramiko.RejectPolicy())
482
483 self.hostname = hostname
484 self.port = port
485
486
487 try:
488 self.sftp_client = self.open_sftp()
489 except:
490 raise x2go_exceptions.X2goControlSessionException('could not invoke server-side SFTP subsystem')
491
492
493 ssh_transport = self.get_transport()
494 ssh_transport.reverse_tunnels = {}
495
496
497 ssh_transport._x2go_session_marker = True
498 self._session_password = password
499
500 if self.get_transport():
501 self.session_died = False
502
503 if not self.home_exists():
504 raise x2go_exceptions.X2goRemoteHomeException('remote home directory does not exist')
505
506 return (self.get_transport() is not None)
507
518
558
559
561 (_stdin, _stdout, _stderr) = self._x2go_exec_command('stat -tL "%s"' % self._x2go_remote_home, loglevel=log.loglevel_DEBUG)
562 if _stdout.read():
563 return True
564 return False
565
566
568 if self._x2go_exec_command('echo', loglevel=log.loglevel_DEBUG):
569 return True
570 return False
571
572 - def start(self, **kwargs):
573 """\
574 Start a new X2go session.
575
576 The L{X2goControlSessionSTDOUT.start()} method accepts any parameter
577 that can be passed to any of the C{X2goTerminalSession} backend class
578 constructors.
579
580 """
581 return self.resume(**kwargs)
582
583 - def resume(self, session_name=None, session_instance=None, **kwargs):
584 """\
585 Resume a running/suspended X2go session.
586
587 The L{X2goControlSessionSTDOUT.resume()} method accepts any parameter
588 that can be passed to any of the C{X2goTerminalSession} backend class constructors.
589
590 @return: True if the session could be successfully resumed
591 @rtype: C{bool}
592
593 """
594 if not self.is_x2gouser(self.get_transport().get_username()):
595 raise x2go_exceptions.X2goUserException('remote user %s is not allowed to run X2go commands' % self.get_transport().get_username())
596
597 if session_name is not None:
598 session_info = self.list_sessions()[session_name]
599 else:
600 session_info = None
601
602 _terminal = self._terminal_backend(self,
603 profile_name=self.profile_name,
604 session_info=session_info,
605 info_backend=self._info_backend,
606 list_backend=self._list_backend,
607 proxy_backend=self._proxy_backend,
608 client_rootdir=self.client_rootdir,
609 session_instance=session_instance,
610 sessions_rootdir=self.sessions_rootdir,
611 **kwargs)
612
613 _success = False
614 if session_name is not None:
615 try:
616 _success = _terminal.resume()
617 except x2go_exceptions.X2goFwTunnelException:
618 pass
619
620 else:
621 try:
622 _success = _terminal.start()
623 except x2go_exceptions.X2goFwTunnelException:
624 pass
625
626 if _success:
627 while not _terminal.ok():
628 gevent.sleep(.2)
629
630 if _terminal.ok():
631 self.associated_terminals[_terminal.get_session_name()] = _terminal
632 self.get_transport().reverse_tunnels[_terminal.get_session_name()] = {
633 'sshfs': (0, None),
634 'snd': (0, None),
635 }
636
637 return _terminal or None
638
639 return None
640
641 - def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, **kwargs):
642 """\
643 Share another already running desktop session. Desktop sharing can be run
644 in two different modes: view-only and full-access mode.
645
646 @param desktop: desktop ID of a sharable desktop in format <user>@<display>
647 @type desktop: C{str}
648 @param user: user name and display number can be given separately, here give the
649 name of the user who wants to share a session with you.
650 @type user: C{str}
651 @param display: user name and display number can be given separately, here give the
652 number of the display that a user allows you to be shared with.
653 @type display: C{str}
654 @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS.
655 @type share_mode: C{int}
656
657 @return: True if the session could be successfully shared.
658 @rtype: C{bool}
659
660 """
661 if desktop:
662 user = desktop.split('@')[0]
663 display = desktop.split('@')[1]
664 if not (user and display):
665 raise x2go_exceptions.X2goDesktopSharingException('Need user name and display number of sharable desktop.')
666
667 cmd = '%sXSHAD%sXSHAD%s' % (share_mode, user, display)
668
669 kwargs['cmd'] = cmd
670 kwargs['session_type'] = 'shared'
671
672 return self.start(**kwargs)
673
675 """\
676 List all desktop-like sessions of current user (or of users that have
677 granted desktop sharing) on the connected server.
678
679 @param raw: if C{True}, the raw output of the server-side X2go command
680 C{x2godesktopsharing} is returned.
681 @type raw: C{bool}
682
683 @return: a list of X2go desktops available for sharing
684 @rtype: C{list}
685
686 """
687 if raw:
688 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops")
689 return stdout.read(), stderr.read()
690
691 else:
692
693
694
695
696 timeout = gevent.Timeout(maxwait)
697 timeout.start()
698 try:
699 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops")
700 _stdout_read = stdout.read()
701 _listdesktops = _stdout_read.split('\n')
702 except gevent.timeout.Timeout:
703
704
705
706 raise x2go_exceptions.X2goTimeOutException('x2golistdesktop command timed out')
707 finally:
708 timeout.cancel()
709
710 return _listdesktops
711
713 """\
714 List all sessions of current user on the connected server.
715
716 @param raw: if C{True}, the raw output of the server-side X2go command
717 C{x2golistsessions} is returned.
718 @type raw: C{bool}
719
720 @return: normally an instance of a C{X2goServerSessionList} backend Bis returned. However,
721 if the raw argument is set, the plain text output of the x2golistsessions
722 command is returned
723 @rtype: C{X2goServerSessionList} instance or str
724
725 """
726 if raw:
727 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
728 return stdout.read(), stderr.read()
729
730 else:
731
732
733
734 _listsessions = {}
735 _success = False
736 _count = 0
737 _maxwait = 20
738
739
740
741 while not _success and _count < _maxwait:
742 _count += 1
743 try:
744 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
745 _stdout_read = stdout.read()
746 _listsessions = self._list_backend(_stdout_read, info_backend=self._info_backend).sessions
747 _success = True
748 except KeyError:
749 gevent.sleep(1)
750 except IndexError:
751 gevent.sleep(1)
752 except ValueError:
753 gevent.sleep(1)
754
755 if _count >= _maxwait:
756 raise x2go_exceptions.X2goControlSessionException('x2golistsessions command failed after we have tried 20 times')
757
758
759 for _session_name, _session_info in self.associated_terminals.items():
760 if _session_name not in _listsessions.keys():
761 del self.associated_terminals[_session_name]
762 self.terminated_terminals.append(_session_name)
763 elif _session_info.is_suspended():
764 del self.associated_terminals[_session_name]
765
766 return _listsessions
767
769 """\
770 Find X2go terminals that have previously been started by the
771 connected user on the remote X2go server and terminate them.
772
773 """
774 session_list = self.list_sessions()
775 for session_name in session_list.keys():
776 self.terminate(session_name=session_name, destroy_terminals=destroy_terminals)
777
779 """\
780 Returns C{True} if this X2go session is connected to the remote server (that
781 is if it has a valid Paramiko Transport object).
782
783 @return: X2go session connected?
784 @rtype: C{bool}
785
786 """
787 return self.get_transport() is not None and self.get_transport().is_authenticated()
788
790 """\
791 Returns C{True} if the given X2go session is in running state,
792 C{False} else.
793
794 @param session_name: X2go name of the session to be queried
795 @type session_name: str
796
797 @return: X2go session running?
798 @rtype: C{bool}
799
800 """
801 session_infos = self.list_sessions()
802 if session_name in session_infos.keys():
803 return session_infos[session_name].is_running()
804 return None
805
807 """\
808 Returns C{True} if the given X2go session is in suspended state,
809 C{False} else.
810
811 @return: X2go session suspended?
812 @rtype: C{bool}
813
814 """
815 session_infos = self.list_sessions()
816 if session_name in session_infos.keys():
817 return session_infos[session_name].is_suspended()
818 return None
819
821 """\
822 Returns C{True} if this X2go session is not in the session list on the
823 connected server, C{False} else.
824
825 Of course, if this command is called before session startup, it will also
826 return C{True}.
827
828 @return: X2go session has terminate?
829 @rtype: C{bool}
830
831 """
832 session_infos = self.list_sessions()
833 if session_name not in session_infos.keys():
834 if session_name in self.terminated_terminals:
835 return True
836 else:
837
838 if session_name in self.associated_terminals.keys():
839 self.terminate(session_name)
840 return True
841 return False
842
844 """\
845 Suspend either this or another available X2go session on the connected
846 server.
847
848 If L{session_name} is given, L{X2goControlSessionSTDOUT.suspend()} tries to suspend the
849 corresponding session.
850
851 @param session_name: X2go name of the session to be suspended
852 @type session_name: str
853
854 @return: True if the session could be successfully suspended
855 @rtype: C{bool}
856
857 """
858 _ret = False
859 _session_names = [ t.get_session_name() for t in self.associated_terminals.values() ]
860 if session_name in _session_names:
861
862 self.logger('suspending associated terminal session: %s' % session_name, loglevel=log.loglevel_DEBUG)
863 (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG)
864 dummy_stdout = stdout.read()
865 dummy_stderr = stderr.read()
866 if self.associated_terminals.has_key(session_name):
867 if self.associated_terminals[session_name] is not None:
868 self.associated_terminals[session_name].__del__()
869 try: del self.associated_terminals[session_name]
870 except KeyError: pass
871 _ret = True
872
873 else:
874
875 self.logger('suspending non-associated terminal session: %s' % session_name, loglevel=log.loglevel_DEBUG)
876 (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG)
877 dummy_stdout = stdout.read()
878 dummy_stderr = stderr.read()
879 _ret = True
880
881 return _ret
882
883 - def terminate(self, session_name, destroy_terminals=True):
884 """\
885 Terminate either this or another available X2go session on the connected
886 server.
887
888 If L{session_name} is given, L{X2goControlSessionSTDOUT.terminate()} tries to terminate the
889 corresponding session.
890
891 @param session_name: X2go name of the session to be terminated
892 @type session_name: str
893
894 @return: True if the session could be successfully terminate
895 @rtype: C{bool}
896
897 """
898
899 _ret = False
900 _session_names = [ t.get_session_name() for t in self.associated_terminals.values() ]
901 if session_name in _session_names:
902
903 self.logger('terminating associated session: %s' % session_name, loglevel=log.loglevel_DEBUG)
904 (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG)
905 dummy_stdout = stdout.read()
906 dummy_stderr = stderr.read()
907 if self.associated_terminals.has_key(session_name):
908 if self.associated_terminals[session_name] is not None and destroy_terminals:
909 self.associated_terminals[session_name].__del__()
910 try: del self.associated_terminals[session_name]
911 except KeyError: pass
912 self.terminated_terminals.append(session_name)
913 _ret = True
914
915 else:
916
917 self.logger('terminating non-associated session: %s' % session_name, loglevel=log.loglevel_DEBUG)
918 (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG)
919 dummy_stdout = stdout.read()
920 dummy_stderr = stderr.read()
921 _ret = True
922
923 return _ret
924