1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """\
21 X2goTerminalSession class - core functions for handling your individual X2go sessions.
22
23 This backend handles X2go server implementations that respond with session infos
24 via server-side STDOUT and use NX3 as graphical proxy.
25
26 """
27 __NAME__ = 'x2goterminalsession-pylib'
28
29
30 import os
31 import sys
32 import types
33 import gevent
34 import threading
35 import signal
36 import cStringIO
37 import copy
38 import shutil
39
40
41 import x2go.rforward as rforward
42 import x2go.sftpserver as sftpserver
43 import x2go.printqueue as printqueue
44 import x2go.mimebox as mimebox
45 import x2go.log as log
46 import x2go.defaults as defaults
47 import x2go.utils as utils
48 import x2go.x2go_exceptions as x2go_exceptions
49
50 from x2go.cleanup import x2go_cleanup
51
52
53 from x2go.defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
54 from x2go.defaults import LOCAL_HOME as _LOCAL_HOME
55 from x2go.defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER
56 from x2go.defaults import X2GO_CLIENT_ROOTDIR as _X2GO_CLIENT_ROOTDIR
57 from x2go.defaults import X2GO_SESSIONS_ROOTDIR as _X2GO_SESSIONS_ROOTDIR
58 from x2go.defaults import X2GO_GENERIC_APPLICATIONS as _X2GO_GENERIC_APPLICATIONS
59
60 from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo
61 from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList
62 from x2go.backends.proxy import X2goProxy as _X2goProxy
63 from x2go.backends.printing import X2goClientPrinting as _X2goClientPrinting
64
65 _local_color_depth = utils.local_color_depth()
66
68
69
70 cmd = cmd or ''
71
72
73 if cmd in defaults.X2GO_DESKTOPSESSIONS.keys():
74 cmd = defaults.X2GO_DESKTOPSESSIONS[cmd]
75
76 if (cmd == 'RDP') and (type(params) == X2goSessionParams):
77 if params.geometry == 'fullscreen':
78 cmd = 'rdesktop -f -N %s %s -a %s' % (params.rdp_options, params.rdp_server, params.depth)
79 else:
80 cmd = 'rdesktop -g %s -N %s %s -a %s' % (params.geometry, params.rdp_options, params.rdp_server, params.depth)
81
82
83 if cmd:
84 cmd = '"%s"' % cmd
85 return cmd
86
87
89
90 if cmd:
91 cmd = cmd.replace(" ", "X2GO_SPACE_CHAR")
92 return cmd
93
94
96 """\
97 The L{X2goSessionParams} class is used to store all parameters that
98 C{X2goTerminalSession} backend objects are constructed with.
99
100 """
102 """\
103 Rewrite the X2go session type, so that the X2go server
104 can understand it (C{desktop} -> C{D}).
105
106 Also if the object's C{command} property is a known window
107 manager, the session type will be set to 'D'
108 (i.e. desktop).
109
110 @return: 'D' if session should probably a desktop session,
111 'R' (for rootless) else
112 @rtype: str
113
114 """
115 session_type = self.session_type
116 cmd = self.cmd
117
118 if session_type in ("D", "desktop"):
119 self.session_type = 'D'
120 return
121 elif session_type in ("S", "shared", "shadow"):
122 self.session_type = 'S'
123 return
124 elif cmd:
125 if cmd == 'RDP':
126 self.session_type = 'R'
127 return
128 elif cmd.startswith('rdesktop'):
129 self.session_type = 'R'
130 return
131 elif cmd == 'XDMCP':
132 self.session_type = 'D'
133 return
134 elif cmd in defaults.X2GO_DESKTOPSESSIONS.keys():
135 self.session_type = 'D'
136 return
137 elif os.path.basename(cmd) in defaults.X2GO_DESKTOPSESSIONS.values():
138 self.session_type = 'D'
139 return
140 self.session_type = 'R'
141
142 - def update(self, properties_to_be_updated={}):
143 """\
144 Update all properties in the object L{X2goSessionParams} object from
145 the passed on dictionary.
146
147 @param properties_to_be_updated: a dictionary with L{X2goSessionParams}
148 property names as keys und their values to be update in
149 L{X2goSessionParams} object.
150 @type properties_to_be_updated: dict
151
152 """
153 for key in properties_to_be_updated.keys():
154 setattr(self, key, properties_to_be_updated[key] or '')
155 self.rewrite_session_type()
156
157
159 """\
160 Class for managing X2go sessions on a remote X2go server via Paramiko/SSH.
161 With the L{X2goTerminalSessionSTDOUT} class you can start new X2go sessions, resume suspended
162 sessions or suspend resp. terminate currently running sessions on a
163 connected X2go server.
164
165 When suspending or terminating sessions there are two possible ways:
166
167 1. Initialize an X2go session object, start a new session (or resume)
168 and use the L{X2goTerminalSessionSTDOUT.suspend()} or L{X2goTerminalSessionSTDOUT.terminate()} method
169 to suspend/terminate the current session object.
170 2. Alternatively, you can pass a session name to L{X2goTerminalSessionSTDOUT.suspend()}
171 or L{X2goTerminalSessionSTDOUT.terminate()}. If a session of this name exists on the
172 X2go server the respective action will be performed on the session.
173
174 An L{X2goTerminalSessionSTDOUT} object uses two main data structure classes:
175
176 - L{X2goSessionParams}: stores all parameters that have been passed to the
177 constructor method.
178
179 - C{X2goServerSessionInfo} backend class: when starting or resuming a session, an object of this class
180 will be used to store all information retrieved from the X2go server.
181
182
183 """
184 - def __init__(self, control_session, session_info=None,
185 geometry="800x600", depth=_local_color_depth, link="adsl", pack="16m-jpeg-9",
186 cache_type="unix-kde",
187 keyboard='', kblayout='null', kbtype='null/null',
188 session_type="application", snd_system='pulse', snd_port=4713, cmd=None,
189 rdp_server=None, rdp_options=None,
190 xdmcp_server=None,
191 convert_encoding=False, server_encoding='UTF-8', client_encoding='UTF-8',
192 rootdir=None,
193 profile_name='UNKNOWN', profile_id=utils._genSessionProfileId(),
194 print_action=None, print_action_args={},
195 info_backend=_X2goServerSessionInfo,
196 list_backend=_X2goServerSessionList,
197 proxy_backend=_X2goProxy, proxy_options={},
198 printing_backend=_X2goClientPrinting,
199 client_rootdir=os.path.join(_LOCAL_HOME, _X2GO_CLIENT_ROOTDIR),
200 sessions_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SESSIONS_ROOTDIR),
201 session_instance=None,
202 logger=None, loglevel=log.loglevel_DEFAULT):
203 """\
204 Initialize an X2go session. With the L{X2goTerminalSessionSTDOUT} class you can start
205 new X2go sessions, resume suspended sessions or suspend resp. terminate
206 currently running sessions on a connected X2go server.
207
208 @param geometry: screen geometry of the X2go session. Can be either C{<width>x<height>}
209 or C{fullscreen}
210 @type geometry: str
211 @param depth: color depth in bits (common values: C{16}, C{24})
212 @type depth: int
213 @param link: network link quality (either one of C{modem}, C{isdn}, C{adsl}, C{wan} or C{lan})
214 @type link: str
215 @param pack: compression method for NX based session proxying
216 @type pack: str
217 @param cache_type: a dummy parameter that is passed to the L{X2goProxyBASE}. In NX Proxy
218 (class C{X2goProxyNX3}) this originally is the session name. With X2go it
219 defines the name of the NX cache directory. Best is to leave it untouched.
220 @type cache_type: str
221 @param kblayout: keyboard layout, e.g. C{us} (default), C{de}, C{fr}, ...
222 @type kblayout: str
223 @param kbtype: keyboard type, e.g. C{pc105/us} (default), C{pc105/de}, ...
224 @type kbtype: str
225 @param session_type: either C{desktop}, C{application} (rootless session) or C{shared}
226 @type session_type: str
227 @param snd_system: sound system to be used on server (C{none}, C{pulse} (default),
228 C{arts} (obsolete) or C{esd})
229 @type snd_system: str
230 @param cmd: command to be run on X2go server after session start (only used
231 when L{X2goTerminalSessionSTDOUT.start()} is called, ignored on resume, suspend etc.
232 @type cmd: str
233 @param rootdir: X2go session directory, normally C{~/.x2go}
234 @type rootdir: str
235 @param info_backend: backend for handling storage of server session information
236 @type info_backend: C{X2goServerSessionInfo*} instance
237 @param list_backend: backend for handling storage of session list information
238 @type list_backend: C{X2goServerSessionList*} instance
239 @param proxy_backend: backend for handling the X-proxy connections
240 @type proxy_backend: C{X2goProxy*} instance
241 @param print_action: either a print action short name (PDFVIEW, PDFSAVE, PRINT, PRINTCMD) or the
242 resp. C{X2goPrintActionXXX} class (where XXX equals one of the given short names)
243 @type print_action: str or class
244 @param print_action_args: optional arguments for a given print_action (for further info refer to
245 L{X2goPrintActionPDFVIEW}, L{X2goPrintActionPDFSAVE}, L{X2goPrintActionPRINT} and L{X2goPrintActionPRINTCMD})
246 @type print_action_args: dict
247 @param proxy_options: a set of very C{X2goProxy*} backend specific options; any option that is not known
248 to the C{X2goProxy*} backend will simply be ignored
249 @type proxy_options: C{dict}
250 @param logger: you can pass an L{X2goLogger} object to the
251 L{X2goTerminalSessionSTDOUT} constructor
252 @type logger: L{X2goLogger} instance
253 @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
254 constructed with the given loglevel
255 @type loglevel: int
256
257 """
258 self.proxy = None
259 self.proxy_subprocess = None
260 self.proxy_options = proxy_options
261
262 self.active_threads = []
263 self.reverse_tunnels = {}
264
265 self.print_queue = None
266 self.mimebox_queue = None
267
268 if logger is None:
269 self.logger = log.X2goLogger(loglevel=loglevel)
270 else:
271 self.logger = copy.deepcopy(logger)
272 self.logger.tag = __NAME__
273
274 self.control_session = control_session
275 self.reverse_tunnels = self.control_session.get_transport().reverse_tunnels
276
277 self.client_rootdir = client_rootdir
278 self.sessions_rootdir = sessions_rootdir
279
280 self.params = X2goSessionParams()
281
282 self.params.geometry = str(geometry)
283 self.params.link = str(link)
284 self.params.pack = str(pack)
285 self.params.cache_type = str(cache_type)
286 self.params.session_type = str(session_type)
287 self.params.keyboard = str(keyboard)
288 self.params.kblayout = str(kblayout)
289 self.params.kbtype = str(kbtype)
290 self.params.snd_system = str(snd_system)
291 self.params.cmd = str(cmd)
292 self.params.depth = str(depth)
293
294 self.params.rdp_server = str(rdp_server)
295 self.params.rdp_options = str(rdp_options)
296 self.params.xdmcp_server = str(xdmcp_server)
297
298 self.params.convert_encoding = convert_encoding
299 self.params.client_encoding = str(client_encoding)
300 self.params.server_encoding = str(server_encoding)
301
302 self.params.rootdir = (type(rootdir) is types.StringType) and rootdir or self.sessions_rootdir
303 self.params.update()
304
305 self.profile_name = profile_name
306 self.proxy_backend = proxy_backend
307
308 self.snd_port = snd_port
309 self.print_action = print_action
310 self.print_action_args = print_action_args
311 self.printing_backend = printing_backend
312 self.session_instance = session_instance
313 if self.session_instance:
314 self.client_instance = self.session_instance.client_instance
315 else:
316 self.client_instance = None
317
318 self._mk_sessions_rootdir(self.params.rootdir)
319
320 self.session_info = session_info
321 if self.session_info is not None:
322 if self.session_info.name:
323 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
324 else:
325 raise X2goTerminalSessionException('no valid session info availble')
326 else:
327 self.session_info = info_backend()
328
329 self._cleaned_up = False
330
333
335
336 self.release_proxy()
337
338 try:
339 if self.control_session.get_transport() is not None:
340 try:
341 for _tunnel in [ _tun[1] for _tun in self.reverse_tunnels[self.session_info.name].values() ]:
342 if _tunnel is not None:
343 _tunnel.__del__()
344 except KeyError:
345 pass
346
347 if self.print_queue is not None:
348 self.print_queue.__del__()
349
350 if self.mimebox_queue is not None:
351 self.mimebox_queue.__del__()
352
353 except AttributeError:
354 pass
355
356 self.session_info.clear()
357
359
360 try:
361 os.mkdir(d)
362 except OSError, e:
363 if e.errno == 17:
364
365 pass
366 else:
367 raise OSError, e
368
373
375
376 if self.session_info.display:
377 shutil.rmtree('%s/S-%s' % (self.params.rootdir, self.session_info.display), ignore_errors=True)
378
385
387 """\
388 Initialize Paramiko/SSH reverse forwarding tunnel for X2go sound.
389
390 Currently supported audio protocols:
391
392 - PulseAudio
393 - Esound
394
395 """
396 _tunnel = None
397 if self.reverse_tunnels[self.session_info.name]['snd'][1] is None:
398 if self.params.snd_system == 'pulse':
399 self.logger('initializing PulseAudio sound support in X2go session', loglevel=log.loglevel_INFO)
400
401
402
403 if os.path.exists(os.path.normpath('%s/.pulse-cookie' % _LOCAL_HOME)):
404
405 cmd_line = "echo 'default-server=127.0.0.1:%s'>%s/.pulse-client.conf;" % (self.session_info.snd_port, self.session_info.remote_container) + \
406 "echo 'cookie-file=%s/.pulse-cookie'>>%s/.pulse-client.conf" % (self.session_info.remote_container, self.session_info.remote_container)
407 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
408
409 self.control_session._x2go_sftp_put(local_path='%s/.pulse-cookie' % _LOCAL_HOME, remote_path='%s/.pulse-cookie' % self.session_info.remote_container)
410
411
412 _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port,
413 remote_host='127.0.0.1',
414 remote_port=self.snd_port,
415 ssh_transport=self.control_session.get_transport(),
416 session_instance=self.session_instance,
417 logger=self.logger
418 )
419 else:
420 if self.client_instance:
421 self.client_instance.HOOK_on_sound_tunnel_failed(profile_name=self.profile_name, session_name=self.session_info.name)
422 elif self.params.snd_system == 'arts':
423
424
425
426 self.logger('the ArtsD sound server (as in KDE3) is obsolete and will not be supported by Python X2go...', loglevel=log.loglevel_WARNING)
427
428 elif self.params.snd_system == 'esd':
429
430
431
432
433 self.logger('initializing ESD sound support in X2go session', loglevel=log.loglevel_INFO)
434 self.control_session._x2go_sftp_put(local_path='%s/.esd_auth' % _LOCAL_HOME, remote_path='%s/.esd_auth' % self.control_session._x2go_remote_home)
435
436
437 _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port,
438 remote_host='127.0.0.1',
439 remote_port=self.snd_port,
440 ssh_transport=self.control_session.get_transport(),
441 session_instance=self.session_instance,
442 logger=self.logger
443 )
444
445
446 if _tunnel is not None:
447 self.reverse_tunnels[self.session_info.name]['snd'] = (self.session_info.snd_port, _tunnel)
448 _tunnel.start()
449 self.active_threads.append(_tunnel)
450
451 else:
452
453 self.reverse_tunnels[self.session_info.name]['snd'][1].resume()
454
456 """\
457 Initialize Paramiko/SSH reverse forwarding tunnel for X2go folder sharing.
458
459 """
460 if not self.control_session.is_folder_sharing_available():
461 raise x2go_exceptions.X2goUserException('remote user %s is not member of X2go server group fuse' % self.session_info.username)
462
463
464 ssh_transport = self.control_session.get_transport()
465 if self.reverse_tunnels[self.session_info.name]['sshfs'][1] is None:
466
467 _tunnel = sftpserver.X2goRevFwTunnelToSFTP(server_port=self.session_info.sshfs_port,
468 ssh_transport=ssh_transport,
469 auth_key=self.control_session._x2go_session_auth_rsakey,
470 session_instance=self.session_instance,
471 logger=self.logger
472 )
473
474 if _tunnel is not None:
475 self.reverse_tunnels[self.session_info.name]['sshfs'] = (self.session_info.sshfs_port, _tunnel)
476 _tunnel.start()
477 self.active_threads.append(_tunnel)
478
479 else:
480
481 self.reverse_tunnels[self.session_info.name]['sshfs'][1].resume()
482
484
485 ssh_transport = self.get_transport()
486 _tunnel = self.reverse_tunnels[self.session_info.name][name][1]
487 if _tunnel is not None:
488 _tunnel.pause()
489
491 """\
492 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2go sound.
493
494 """
495 self._x2go_pause_rev_fw_tunnel('snd')
496
498 """\
499 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2go folder sharing.
500
501 """
502 self._x2go_pause_rev_fw_tunnel('sshfs')
503
505 """\
506 Initialize X2go print spooling.
507
508 """
509 if self.session_info.username not in self.control_session._x2go_remote_group('x2goprint'):
510 raise x2go_exceptions.X2goUserException('remote user %s is not member of X2go server group x2goprint' % self.session_info.username)
511
512 spool_dir = os.path.join(self.session_info.local_container, 'spool')
513 if not os.path.exists(spool_dir):
514 os.mkdir(spool_dir)
515 self.share_local_folder(local_path=spool_dir, folder_type='spool')
516 self.print_queue = printqueue.X2goPrintQueue(profile_name=self.profile_name,
517 session_name=self.session_info.name,
518 spool_dir=spool_dir,
519 print_action=self.print_action,
520 print_action_args=self.print_action_args,
521 client_instance=self.client_instance,
522 printing_backend=self.printing_backend,
523 logger=self.logger,
524 )
525 self.print_queue.start()
526 self.active_threads.append(self.print_queue)
527
534
536 """\
537 Shutdown (pause) the X2go Print Queue thread.
538
539 """
540 if self.print_queue is not None:
541 self.print_queue.pause()
542
544 """\
545 Return the server-side printing spooldir path.
546
547 """
548 return '%s/%s' % (self.session_info.remote_container, 'spool')
549
550 - def start_mimebox(self, mimebox_extensions=[], mimebox_action=None):
551 """\
552 Initialize X2go mimebox handling.
553
554 """
555 mimebox_dir = os.path.join(self.session_info.local_container, 'mimebox')
556 if not os.path.exists(mimebox_dir):
557 os.mkdir(mimebox_dir)
558 self.share_local_folder(local_path=mimebox_dir, folder_type='mimebox')
559 self.mimebox_queue = mimebox.X2goMIMEboxQueue(profile_name=self.profile_name,
560 session_name=self.session_info.name,
561 mimebox_dir=mimebox_dir,
562 mimebox_extensions=mimebox_extensions,
563 mimebox_action=mimebox_action,
564 client_instance=self.client_instance,
565 logger=self.logger,
566 )
567 self.mimebox_queue.start()
568 self.active_threads.append(self.mimebox_queue)
569
576
578 """\
579 Shutdown (pause) the X2go MIME box Queue thread.
580
581 """
582 if self.mimebox_queue is not None:
583 self.mimebox_queue.pause()
584
586 """\
587 Return the server-side mimebox spooldir path.
588
589 """
590 return '%s/%s' % (self.session_info.remote_container, 'mimebox')
591
593 """\
594 Share a local folder with the X2go session.
595
596 @param local_path: the full path to an existing folder on the local
597 file system
598 @type local_path: str
599 @param folder_type: one of 'disk' (a folder on your local hard drive), 'rm' (removeable device),
600 'cdrom' (CD/DVD Rom) or 'spool' (for X2go print spooling)
601 @type folder_type: str
602
603 @return: returns C{True} if the local folder has been successfully mounted within the X2go server session
604 @rtype: bool
605
606 """
607 if not self.control_session.is_folder_sharing_available():
608 raise x2go_exceptions.X2goUserException('remote user %s is not member of X2go server group fuse' % self.session_info.username)
609
610 if local_path is None:
611 self.logger('no folder name given...', log.loglevel_WARN)
612 return False
613
614 if type(local_path) not in (types.StringType, types.UnicodeType):
615 self.logger('folder name needs to be of type StringType...', log.loglevel_WARN)
616 return False
617
618 if not os.path.exists(local_path):
619 self.logger('local folder does not exist: %s' % local_path, log.loglevel_WARN)
620 return False
621
622 local_path = os.path.normpath(local_path)
623 self.logger('sharing local folder: %s' % local_path, log.loglevel_INFO)
624
625 _auth_rsakey = self.control_session._x2go_session_auth_rsakey
626 _host_rsakey = defaults.RSAHostKey
627
628 _tmp_io_object = cStringIO.StringIO()
629 _auth_rsakey.write_private_key(_tmp_io_object)
630 _tmp_io_object.write('----BEGIN RSA IDENTITY----')
631 _tmp_io_object.write('%s %s' % (_host_rsakey.get_name(),_host_rsakey.get_base64(),))
632
633
634 _x2go_key_fname = '%s/%s/%s' % (os.path.dirname(self.session_info.remote_container), 'ssh', 'key.z%s' % self.session_info.agent_pid)
635 _x2go_key_bundle = _tmp_io_object.getvalue()
636
637 self.control_session._x2go_sftp_write(_x2go_key_fname, _x2go_key_bundle)
638
639 _convert_encoding = self.params.convert_encoding
640 _client_encoding = self.params.client_encoding
641 _server_encoding = self.params.server_encoding
642
643 if _X2GOCLIENT_OS == 'Windows':
644 local_path = local_path.replace('\\', '/')
645 local_path = local_path.replace(':', '')
646 local_path = '/windrive/%s' % local_path
647 _convert_encoding = True
648 _client_encoding = 'WINDOWS-1252'
649
650 if _convert_encoding:
651 export_iconv_settings = 'export X2GO_ICONV=modules=iconv,from_code=%s,to_code=%s &&' % (_client_encoding, _server_encoding)
652 else:
653 export_iconv_settings = ''
654
655 if folder_type == 'disk':
656
657 cmd_line = [ '%s export HOSTNAME &&' % export_iconv_settings,
658 'x2gomountdirs',
659 'dir',
660 str(self.session_info.name),
661 '"%s"' % _CURRENT_LOCAL_USER,
662 _x2go_key_fname,
663 '%s__REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
664 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
665 ]
666
667 elif folder_type == 'spool':
668
669 cmd_line = [ '%s export HOSTNAME &&' % export_iconv_settings,
670 'x2gomountdirs',
671 'dir',
672 str(self.session_info.name),
673 '"%s"' % _CURRENT_LOCAL_USER,
674 _x2go_key_fname,
675 '%s__PRINT_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
676 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
677 ]
678
679 elif folder_type == 'mimebox':
680
681 cmd_line = [ '%s export HOSTNAME &&' % export_iconv_settings,
682 'x2gomountdirs',
683 'dir',
684 str(self.session_info.name),
685 '"%s"' % _CURRENT_LOCAL_USER,
686 _x2go_key_fname,
687 '%s__MIMEBOX_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port),
688 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname),
689 ]
690
691 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
692 _stdout = stdout.read().split('\n')
693 self.logger('x2gomountdirs output is : %s' % _stdout, log.loglevel_NOTICE)
694 if _stdout[5].endswith('ok'):
695 return True
696 return False
697
699 """\
700 Unshare all local folders mount in the X2go session.
701
702 @return: returns C{True} if all local folders could be successfully unmounted from the X2go server session
703 @rtype: bool
704
705 """
706 self.logger('unsharing all local folders from session %s' % self.session_info, log.loglevel_INFO)
707
708 cmd_line = [ 'export HOSTNAME &&',
709 'x2goumount-session',
710 self.session_info.name,
711 ]
712
713 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
714 if not stderr.read():
715 self.logger('x2goumount-session (all mounts) for session %s has been successful' % self.session_info, log.loglevel_NOTICE)
716 return True
717 else:
718 self.logger('x2goumount-session (all mounts) for session %s failed' % self.session_info, log.loglevel_ERROR)
719 return False
720
722 """\
723 Unshare local folder given as <local_path> from X2go session.
724
725 @return: returns C{True} if the local folder <local_path> could be successfully unmounted from the X2go server session
726 @rtype: bool
727
728 """
729 self.logger('unsharing local folder from session %s' % self.session_info, log.loglevel_INFO)
730
731 cmd_line = [ 'export HOSTNAME &&',
732 'x2goumount-session',
733 self.session_info.name,
734 local_path,
735 ]
736
737 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
738 if not stderr.read():
739 self.logger('x2goumount-session (%s) for session %s has been successful' % (local_path, self.session_info, ), log.loglevel_NOTICE)
740 return True
741 else:
742 self.logger('x2goumount-session (%s) for session %s failed' % (local_path, self.session_info, ), log.loglevel_ERROR)
743 return False
744
746 """\
747 Retrieve the session's color depth.
748
749 @return: the session's color depth
750 @rtype: C{int}
751
752 """
753 return self.params.depth
754
756 """\
757 Verify if the command <cmd> exists on the X2go server.
758
759 """
760 test_cmd = None;
761
762 cmd = cmd.strip('"').strip('"')
763 if cmd.find('RDP') != -1:
764 cmd = 'rdesktop'
765
766 if cmd in _X2GO_GENERIC_APPLICATIONS:
767 return True
768 elif 'XSHAD' in cmd:
769 return True
770 elif cmd and cmd.startswith('/'):
771
772 test_cmd = 'test -x %s && which %s && echo OK' % (cmd, os.path.basename(cmd.split()[0]))
773 elif cmd and '/' not in cmd.split()[0]:
774
775 test_cmd = 'which %s && echo OK' % os.path.basename(cmd.split()[0])
776
777 if test_cmd:
778 (stdin, stdout, stderr) = self.control_session._x2go_exec_command([test_cmd])
779 _stdout = stdout.read()
780 return _stdout.find('OK') != -1
781 else:
782 return False
783
785 """\
786 Run a command in this session.
787
788 After L{X2goTerminalSessionSTDOUT.start()} has been called
789 one or more commands can be executed with L{X2goTerminalSessionSTDOUT.run_command()}
790 within the current X2go session.
791
792 @param cmd: Command to be run
793 @type cmd: str
794
795 @return: stdout.read() and stderr.read() as returned by the run command
796 on the X2go server
797 @rtype: tuple of str
798
799 """
800 if not self.has_command(_rewrite_cmd(self.params.cmd)):
801 if self.client_instance:
802 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd)
803 return False
804
805 if cmd in ("", None):
806 if self.params.cmd is None:
807 cmd = 'TERMINAL'
808 else:
809 cmd = self.params.cmd
810
811 if cmd == 'XDMCP':
812
813 return None
814
815 if 'XSHAD' in cmd:
816
817 return None
818
819 self.params.update({'cmd': cmd})
820
821
822 if '/' in cmd:
823 cmd = os.path.basename(cmd)
824
825 cmd_line = [ "setsid x2goruncommand",
826 str(self.session_info.display),
827 str(self.session_info.agent_pid),
828 str(self.session_info.name),
829 str(self.session_info.snd_port),
830 _rewrite_blanks(_rewrite_cmd(cmd, params=self.params)),
831 str(self.params.snd_system),
832 str(self.params.session_type),
833 "&> /dev/null & exit",
834 ]
835
836 if self.params.snd_system == 'pulse':
837 cmd_line = [ 'PULSE_CLIENTCONFIG=%s/.pulse-client.conf' % self.session_info.remote_container ] + cmd_line
838
839 if env:
840 for env_var in env.keys():
841 cmd_line = [ '%s=%s' % (env_var, env[env_var]) ] + cmd_line
842
843 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
844
845 return stdout.read(), stderr.read()
846
848 """\
849 Returns C{True} if this X2go session is up and running,
850 C{False} else
851
852 @return: X2go session OK?
853 @rtype: bool
854
855 """
856 return bool(self.session_info.name and self.proxy.ok())
857
859 """\
860 Returns C{True} if this X2go session is in running state,
861 C{False} else.
862
863 @return: X2go session running?
864 @rtype: bool
865
866 """
867 return self.session_info.is_running()
868
870 """\
871 Returns C{True} if this X2go session is in suspended state,
872 C{False} else.
873
874 @return: X2go session suspended?
875 @rtype: bool
876
877 """
878 return self.session_info.is_suspended()
879
881 """\
882 Returns C{True} if this X2go session's Paramiko/SSH transport is
883 connected/authenticated, C{False} else.
884
885 @return: X2go session connected?
886 @rtype: bool
887 """
888 return self.control_session.is_connected()
889
891 """\
892 Start a new X2go session.
893
894 The L{X2goTerminalSession.start()} method accepts any parameter
895 that can be passed to the class constructor.
896
897 """
898 if not self.has_command(_rewrite_cmd(self.params.cmd)):
899 if self.client_instance:
900 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd)
901 return False
902
903 setkbd = "0"
904 if self.params.kblayout or self.params.kbtype:
905 setkbd = "1"
906
907 cmd = self.params.cmd
908 if '/' in cmd:
909 cmd = os.path.basename(cmd)
910
911 cmd_line = [ "x2gostartagent",
912 str(self.params.geometry),
913 str(self.params.link),
914 str(self.params.pack),
915 str(self.params.cache_type+'-depth_'+self.params.depth),
916 str(self.params.kblayout),
917 str(self.params.kbtype),
918 str(setkbd),
919 str(self.params.session_type),
920 cmd,
921 ]
922
923 if self.params.cmd == 'XDMCP' and self.params.xdmcp_server:
924 cmd_line = ['X2GOXDMCP=%s' % self.params.xdmcp_server] + cmd_line
925
926 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
927
928 _stdout = stdout.read()
929 _stderr = stderr.read()
930
931
932
933 if "ACCESS DENIED" in _stderr and "XSHAD" in _stderr:
934 raise x2go_exceptions.X2goDesktopSharingException('X2go desktop sharing has been denied by the remote user')
935
936 try:
937 self.session_info.initialize(_stdout,
938 username=self.control_session.remote_username(),
939 hostname=self.control_session.get_transport().getpeername(),
940 )
941 except ValueError:
942 raise X2goTerminalSessionException("failed to start X2go session")
943 except IndexError:
944 raise X2goTerminalSessionException("failed to start X2go session")
945
946
947 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
948
949 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home,
950 self.session_info.name,
951 )
952
953
954 if (self.params.kbtype.endswith('defkeymap') and self.params.kblayout == 'defkeymap'):
955 self.proxy_options.update({'defkeymap': True, })
956
957
958 self.proxy = self.proxy_backend(session_info=self.session_info,
959 ssh_transport=self.control_session.get_transport(),
960 sessions_rootdir=self.sessions_rootdir,
961 session_instance=self.session_instance,
962 proxy_options=self.proxy_options,
963 logger=self.logger)
964 self.proxy_subprocess = self.proxy.start_proxy()
965 self.active_threads.append(self.proxy)
966
967 return self.ok()
968
970 """\
971 Resume a running/suspended X2go session.
972
973 The L{X2goTerminalSessionSTDOUT.resume()} method accepts any parameter
974 that can be passed to the class constructor.
975
976 @return: True if the session could be successfully resumed
977 @rtype: bool
978
979 """
980 setkbd = "0"
981 if self.params.kblayout or self.params.kbtype:
982 setkbd = "1"
983
984 cmd_line = [ "x2goresume-session", self.session_info.name,
985 self.params.geometry,
986 self.params.link,
987 self.params.pack,
988 self.params.kblayout,
989 self.params.kbtype,
990 setkbd,
991 ]
992
993 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line)
994
995 self.proxy = self.proxy_backend(session_info=self.session_info,
996 ssh_transport=self.control_session.get_transport(),
997 sessions_rootdir=self.sessions_rootdir,
998 session_instance=self.session_instance,
999 logger=self.logger
1000 )
1001 self.proxy_subprocess = self.proxy.start_proxy()
1002
1003
1004 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name)
1005
1006 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home,
1007 self.session_info.name,
1008 )
1009 self.params.depth = self.session_info.name.split('_')[2][2:]
1010
1011 self.session_info.username = self.control_session.remote_username()
1012 return self.ok()
1013
1015 """\
1016 Suspend this X2go session terminal.
1017
1018 @return: True if the session terminal could be successfully suspended
1019 @rtype: bool
1020
1021 """
1022 self.control_session.suspend(session_name=self.session_info.name)
1023 self.release_proxy()
1024
1025
1026 _ret = True
1027
1028 return _ret
1029
1031 """\
1032 Terminate this X2go session.
1033
1034 @return: True if the session terminal could be successfully terminate
1035 @rtype: bool
1036
1037 """
1038 self.control_session.terminate(session_name=self.session_info.name, destroy_terminals=False)
1039 self.release_proxy()
1040 self.post_terminate_cleanup()
1041 self.__del__()
1042
1043
1044 _ret = True
1045
1046 return _ret
1047
1049 """\
1050 STILL UNDOCUMENTED
1051
1052 """
1053 if self.proxy is not None:
1054 self.proxy.__del__()
1055
1057 """\
1058 STILL UNDOCUMENTED
1059
1060 """
1061
1062
1063
1064 if not self._cleaned_up and self.session_info.name:
1065
1066
1067 self.logger('cleaning up session %s after termination' % self.session_info, loglevel=log.loglevel_NOTICE)
1068
1069
1070 if self.logger.get_loglevel() & log.loglevel_DEBUG != log.loglevel_DEBUG:
1071
1072 self._rm_session_dirtree()
1073 self._rm_desktop_dirtree()
1074
1075 self._cleaned_up = True
1076