Package x2go :: Package backends :: Package terminal :: Module _stdout
[frames] | no frames]

Source Code for Module x2go.backends.terminal._stdout

   1  # -*- coding: utf-8 -*- 
   2   
   3  # Copyright (C) 2010-2011 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de> 
   4  # 
   5  # Python X2go is free software; you can redistribute it and/or modify 
   6  # it under the terms of the GNU General Public License as published by 
   7  # the Free Software Foundation; either version 3 of the License, or 
   8  # (at your option) any later version. 
   9  # 
  10  # Python X2go is distributed in the hope that it will be useful, 
  11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  13  # GNU General Public License for more details. 
  14  # 
  15  # You should have received a copy of the GNU General Public License 
  16  # along with this program; if not, write to the 
  17  # Free Software Foundation, Inc., 
  18  # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 
  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  # modules 
  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  # Python X2go modules 
  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  # we hide the default values from epydoc (that's why we transform them to _UNDERSCORE variables) 
  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   
67 -def _rewrite_cmd(cmd, params=None):
68 69 # start with an empty string 70 cmd = cmd or '' 71 72 # find window manager commands 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 # place quot marks around cmd if not empty string 83 if cmd: 84 cmd = '"%s"' % cmd 85 return cmd
86 87
88 -def _rewrite_blanks(cmd):
89 # X2go run command replace X2GO_SPACE_CHAR string with blanks 90 if cmd: 91 cmd = cmd.replace(" ", "X2GO_SPACE_CHAR") 92 return cmd
93 94
95 -class X2goSessionParams(object):
96 """\ 97 The L{X2goSessionParams} class is used to store all parameters that 98 C{X2goTerminalSession} backend objects are constructed with. 99 100 """
101 - def rewrite_session_type(self):
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
158 -class X2goTerminalSessionSTDOUT(object):
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
331 - def __del__(self):
332 self._x2go_tidy_up()
333
334 - def _x2go_tidy_up(self):
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
358 - def _mk_sessions_rootdir(self, d):
359 360 try: 361 os.mkdir(d) 362 except OSError, e: 363 if e.errno == 17: 364 # file exists 365 pass 366 else: 367 raise OSError, e
368
369 - def _rm_session_dirtree(self):
370 371 if self.session_info.name: 372 shutil.rmtree('%s/S-%s' % (self.params.rootdir, self.session_info), ignore_errors=True)
373
374 - def _rm_desktop_dirtree(self):
375 376 if self.session_info.display: 377 shutil.rmtree('%s/S-%s' % (self.params.rootdir, self.session_info.display), ignore_errors=True)
378
379 - def get_session_name(self):
380 """\ 381 STILL UNDOCUMENTED 382 383 """ 384 return self.session_info.name
385
386 - def start_sound(self):
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 ### PULSEAUDIO 402 ### 403 if os.path.exists(os.path.normpath('%s/.pulse-cookie' % _LOCAL_HOME)): 404 # setup pulse client config file on X2go server 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 # start reverse SSH tunnel for pulse stream 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 ### ARTSD AUDIO 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 ### ESD AUDIO 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 # start reverse SSH tunnel for pulse stream 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 # tunnel has already been started and might simply need a resume call 453 self.reverse_tunnels[self.session_info.name]['snd'][1].resume()
454
455 - def start_sshfs(self):
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 # start reverse SSH tunnel for sshfs (folder sharing, printing) 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 # tunnel has already been started and might simply need a resume call 481 self.reverse_tunnels[self.session_info.name]['sshfs'][1].resume()
482
483 - def _x2go_pause_rev_fw_tunnel(self, name):
484 # pause reverse SSH tunnel of name <name> 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
490 - def stop_sound(self):
491 """\ 492 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2go sound. 493 494 """ 495 self._x2go_pause_rev_fw_tunnel('snd')
496
497 - def stop_sshfs(self):
498 """\ 499 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2go folder sharing. 500 501 """ 502 self._x2go_pause_rev_fw_tunnel('sshfs')
503
504 - def start_printing(self):
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
528 - def set_print_action(self, print_action, **kwargs):
529 """\ 530 STILL UNDOCUMENTED 531 532 """ 533 self.print_queue.set_print_action(print_action, logger=self.logger, **kwargs)
534
535 - def stop_printing(self):
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
543 - def get_printing_spooldir(self):
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
570 - def set_mimebox_action(self, mimebox_action, **kwargs):
571 """\ 572 STILL UNDOCUMENTED 573 574 """ 575 self.mimebox_queue.set_mimebox_action(mimebox_action, logger=self.logger, **kwargs)
576
577 - def stop_mimebox(self):
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
585 - def get_mimebox_spooldir(self):
586 """\ 587 Return the server-side mimebox spooldir path. 588 589 """ 590 return '%s/%s' % (self.session_info.remote_container, 'mimebox')
591
592 - def share_local_folder(self, local_path=None, folder_type='disk'):
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 # _x2go_key_fname must be a UniX path 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
698 - def unshare_all_local_folders(self):
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
721 - def unshare_local_folder(self, local_path):
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
745 - def color_depth(self):
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
755 - def has_command(self, cmd):
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 # check if full path is correct _and_ if application is in server path 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 # check if application is in server path only 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
784 - def run_command(self, cmd=None, env={}):
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 # do not run command when in XDMCP mode... 813 return None 814 815 if 'XSHAD' in cmd: 816 # do not run command when in DESKTOP SHARING mode... 817 return None 818 819 self.params.update({'cmd': cmd}) 820 821 # do not allow the execution of full path names 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
847 - def ok(self):
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
858 - def is_running(self):
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
869 - def is_suspended(self):
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
880 - def is_connected(self):
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
890 - def start(self):
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 # if the first line of stdout is a "DEN(Y)" string then we will presume that 932 # we tried to use X2go desktop sharing and the sharing was rejected 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 # local path may be a Windows path, so we use the path separator of the local system 947 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name) 948 # remote path is always a UniX path... 949 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home, 950 self.session_info.name, 951 ) 952 953 # let the proxy backend know that we want to define a very special keymap 954 if (self.params.kbtype.endswith('defkeymap') and self.params.kblayout == 'defkeymap'): 955 self.proxy_options.update({'defkeymap': True, }) 956 957 # set up SSH tunnel for X11 graphical elements 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
969 - def resume(self):
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 # local path may be a Windows path, so we use the path separator of the local system 1004 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name) 1005 # remote path is always a UniX path... 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 # on a session resume the user name comes in as a user ID. We have to translate this... 1011 self.session_info.username = self.control_session.remote_username() 1012 return self.ok()
1013
1014 - def suspend(self):
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 # TODO: check if session has really suspended 1026 _ret = True 1027 1028 return _ret
1029
1030 - def terminate(self):
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 # TODO: check if session has really suspended 1044 _ret = True 1045 1046 return _ret
1047
1048 - def release_proxy(self):
1049 """\ 1050 STILL UNDOCUMENTED 1051 1052 """ 1053 if self.proxy is not None: 1054 self.proxy.__del__()
1055
1056 - def post_terminate_cleanup(self):
1057 """\ 1058 STILL UNDOCUMENTED 1059 1060 """ 1061 # this method might be called twice (directly and from update_status in the session 1062 # registry instance. So we have to make sure, that this code will not fail 1063 # if called twice. 1064 if not self._cleaned_up and self.session_info.name: 1065 1066 # otherwise we wipe the session files locally 1067 self.logger('cleaning up session %s after termination' % self.session_info, loglevel=log.loglevel_NOTICE) 1068 1069 # if we run in debug mode, we keep local session directories 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