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-2012 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 Affero 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 Affero General Public License for more details. 
  14  # 
  15  # You should have received a copy of the GNU Affero 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 types 
  32  import gevent 
  33  import cStringIO 
  34  import copy 
  35  import shutil 
  36  import threading 
  37   
  38  # Python X2Go modules 
  39  import x2go.rforward as rforward 
  40  import x2go.sftpserver as sftpserver 
  41  import x2go.printqueue as printqueue 
  42  import x2go.mimebox as mimebox 
  43  import x2go.log as log 
  44  import x2go.defaults as defaults 
  45  import x2go.utils as utils 
  46  import x2go.x2go_exceptions as x2go_exceptions 
  47   
  48  # we hide the default values from epydoc (that's why we transform them to _UNDERSCORE variables) 
  49  from x2go.defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS 
  50  from x2go.defaults import LOCAL_HOME as _LOCAL_HOME 
  51  from x2go.defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER 
  52  from x2go.defaults import X2GO_CLIENT_ROOTDIR as _X2GO_CLIENT_ROOTDIR 
  53  from x2go.defaults import X2GO_SESSIONS_ROOTDIR as _X2GO_SESSIONS_ROOTDIR 
  54  from x2go.defaults import X2GO_GENERIC_APPLICATIONS as _X2GO_GENERIC_APPLICATIONS 
  55  from x2go.defaults import X2GO_DESKTOPSESSIONS as _X2GO_DESKTOPSESSIONS 
  56   
  57  from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo 
  58  from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList 
  59  from x2go.backends.proxy import X2goProxy as _X2goProxy 
  60  from x2go.backends.printing import X2goClientPrinting as _X2goClientPrinting 
  61   
  62  _local_color_depth = utils.local_color_depth() 
  63   
64 -def _rewrite_cmd(cmd, params=None):
65 """\ 66 Mechansim that rewrites X2Go server commands into something that gets understood by 67 the server-side script C{x2goruncommand}. 68 69 @param cmd: the current command for execution (as found in the session profile parameter C{cmd}) 70 @type cmd: C{str} 71 @param params: an session paramter object 72 @type params: L{X2goSessionParams} 73 74 @return: the rewritten command for server-side execution 75 @rtype: C{str} 76 77 """ 78 # start with an empty string 79 cmd = cmd or '' 80 81 # find window manager commands 82 if cmd in _X2GO_DESKTOPSESSIONS.keys(): 83 cmd = _X2GO_DESKTOPSESSIONS[cmd] 84 85 if (cmd == 'RDP') and (type(params) == X2goSessionParams): 86 if params.geometry == 'fullscreen': 87 cmd = 'rdesktop -f -N %s %s -a %s' % (params.rdp_options, params.rdp_server, params.depth) 88 else: 89 cmd = 'rdesktop -g %s -N %s %s -a %s' % (params.geometry, params.rdp_options, params.rdp_server, params.depth) 90 91 # place quot marks around cmd if not empty string 92 if cmd: 93 cmd = '"%s"' % cmd 94 95 if ((type(params) == X2goSessionParams) and params.published_applications and cmd == ''): 96 cmd = 'PUBLISHED' 97 98 return cmd
99 100
101 -def _rewrite_blanks(cmd):
102 """\ 103 In command strings X2Go server scripts expect blanks being rewritten to ,,X2GO_SPACE_CHAR''. 104 105 @param cmd: command that has to be rewritten for passing to the server 106 @type cmd: C{str} 107 108 @return: the command with blanks rewritten to ,,X2GO_SPACE_CHAR'' 109 @rtype: C{str} 110 111 """ 112 # X2Go run command replace X2GO_SPACE_CHAR string with blanks 113 if cmd: 114 cmd = cmd.replace(" ", "X2GO_SPACE_CHAR") 115 return cmd
116 117
118 -class X2goSessionParams(object):
119 """\ 120 The L{X2goSessionParams} class is used to store all parameters that 121 C{X2goTerminalSession} backend objects are constructed with. 122 123 """
124 - def rewrite_session_type(self):
125 """\ 126 Rewrite the X2Go session type, so that the X2Go server 127 can understand it (C{desktop} -> C{D}, etc.). 128 129 Also if the object's C{command} property is a known window 130 manager, the session type will be set to 'D' 131 (i.e. desktop). 132 133 @return: 'D' if session should probably a desktop session, 134 'R' for rootless sessions, 'P' for sessions providing published 135 applications, and 'S' for desktop sharing sessions 136 @rtype: C{str} 137 138 """ 139 cmd = self.cmd 140 published = self.published_applications 141 142 if published and self.cmd in ('', 'PUBLISHED'): 143 self.session_type = 'P' 144 self.cmd = 'PUBLISHED' 145 else: 146 if cmd == 'RDP' or cmd.startswith('rdesktop') or cmd.startswith('xfreedrp'): 147 if self.geometry == 'fullscreen': self.session_type = 'D' 148 else: self.session_type = 'R' 149 elif cmd == 'XDMCP': 150 self.session_type = 'D' 151 elif cmd in _X2GO_DESKTOPSESSIONS.keys(): 152 self.session_type = 'D' 153 elif os.path.basename(cmd) in _X2GO_DESKTOPSESSIONS.values(): 154 self.session_type = 'D' 155 156 if self.session_type in ("D", "desktop"): 157 self.session_type = 'D' 158 elif self.session_type in ("S", "shared", "shadow"): 159 self.session_type = 'S' 160 elif self.session_type in ("R", "rootless", "application"): 161 self.session_type = 'R' 162 elif self.session_type in ("P", "published", "published_applications"): 163 self.session_type = 'P' 164 165 return self.session_type
166
167 - def update(self, **properties_to_be_updated):
168 """\ 169 Update all properties in the object L{X2goSessionParams} object from 170 the passed on dictionary. 171 172 @param properties_to_be_updated: a dictionary with L{X2goSessionParams} 173 property names as keys und their values to be update in 174 L{X2goSessionParams} object. 175 @type properties_to_be_updated: C{dict} 176 177 """ 178 for key in properties_to_be_updated.keys(): 179 setattr(self, key, properties_to_be_updated[key] or '') 180 self.rewrite_session_type()
181 182
183 -class X2goTerminalSessionSTDOUT(object):
184 """\ 185 Class for managing X2Go terminal sessions on a remote X2Go server via Paramiko/SSH. 186 187 With the L{X2goTerminalSessionSTDOUT} class you can start new X2Go sessions, resume suspended 188 sessions or suspend resp. terminate currently running sessions on a 189 connected X2Go server. 190 191 An L{X2goTerminalSessionSTDOUT} object uses two main data structure classes: 192 193 - L{X2goSessionParams}: stores all parameters that have been passed to the 194 constructor method. 195 196 - C{X2goServerSessionInfo*} backend class: when starting or resuming a session, an object of this class 197 will be used to store all information retrieved from the X2Go server. 198 199 The terminal session instance works closely together (i.e. depends on) a connected control 200 session instance (e.g. L{X2goControlSessionSTDOUT}). You never should use either of them as a standalone 201 instance. Both, control session and terminal session(s) get managed/controlled via L{X2goSession} instances. 202 203 """
204 - def __init__(self, control_session, session_info=None, 205 geometry="800x600", depth=_local_color_depth, link="adsl", pack="16m-jpeg-9", dpi='', 206 cache_type="unix-kde", 207 kbtype='null/null', kblayout='null', kbvariant='null', 208 session_type="application", snd_system='pulse', snd_port=4713, cmd=None, 209 published_applications=False, 210 set_session_title=False, session_title="", applications=[], 211 rdp_server=None, rdp_options=None, 212 xdmcp_server=None, 213 convert_encoding=False, server_encoding='UTF-8', client_encoding='UTF-8', 214 rootdir=None, 215 profile_name='UNKNOWN', profile_id=utils._genSessionProfileId(), 216 print_action=None, print_action_args={}, 217 info_backend=_X2goServerSessionInfo, 218 list_backend=_X2goServerSessionList, 219 proxy_backend=_X2goProxy, proxy_options={}, 220 printing_backend=_X2goClientPrinting, 221 client_rootdir=os.path.join(_LOCAL_HOME, _X2GO_CLIENT_ROOTDIR), 222 sessions_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SESSIONS_ROOTDIR), 223 session_instance=None, 224 logger=None, loglevel=log.loglevel_DEFAULT):
225 """\ 226 Initialize an X2Go session. With the L{X2goTerminalSessionSTDOUT} class you can start 227 new X2Go sessions, resume suspended sessions or suspend resp. terminate 228 currently running sessions on a connected X2Go server. 229 230 @param geometry: screen geometry of the X2Go session. Can be either C{<width>x<height>}, 231 C{maximize} or C{fullscreen} 232 @type geometry: C{str} 233 @param depth: color depth in bits (common values: C{16}, C{24}) 234 @type depth: C{int} 235 @param link: network link quality (either one of C{modem}, C{isdn}, C{adsl}, C{wan} or C{lan}) 236 @type link: C{str} 237 @param pack: compression method for NX based session proxying 238 @type pack: C{str} 239 @param dpi: dots-per-inch value for the session screen (has an impact on the font size on screen) 240 @type dpi: C{str} 241 @param cache_type: a dummy parameter that is passed to the L{X2goProxyBASE}. In NX Proxy 242 (class C{X2goProxyNX3}) this originally is the session name. With X2Go it 243 defines the name of the NX cache directory. Best is to leave it untouched. 244 @type cache_type: C{str} 245 @param kbtype: keyboard type, e.g. C{pc105/us} (default), C{pc105/de}, ... 246 @type kbtype: C{str} 247 @param kblayout: keyboard layout, e.g. C{us} (default), C{de}, C{fr}, ... 248 @type kblayout: C{str} 249 @param kbvariant: keyboard variant, e.g. C{nodeadkeys} (for C{de} layout), C{intl} (for C{us} layout), etc. 250 @type kbvariant: C{str} 251 @param session_type: either C{desktop}, C{application} (rootless session) or C{shared} 252 @type session_type: C{str} 253 @param snd_system: sound system to be used on server (C{none}, C{pulse} (default), 254 C{arts} (obsolete) or C{esd}) 255 @type snd_system: C{str} 256 @param snd_port: local sound port for network capable audio system 257 @type snd_port: C{int} 258 @param cmd: command to be run on X2Go server after session start (only used 259 when L{X2goTerminalSessionSTDOUT.start()} is called, ignored on resume, suspend etc. 260 @type cmd: C{str} 261 @param published_applications: session is published applications provider 262 @type published_applications: C{bool} 263 @param set_session_title: modify the session title (i.e. the Window title) of desktop or shared desktop sessions 264 @type set_session_title: C{bool} 265 @param session_title: session title for this (desktop or shared desktop) session 266 @type session_title: C{str} 267 @param applications: applications available for rootless application execution 268 @type applications: C{list} 269 @param rdp_server: host name of server-side RDP server 270 @type rdp_server: C{str} 271 @param rdp_options: options for the C{rdesktop} command executed on the X2Go server (RDP proxy mode of X2Go) 272 @type rdp_options: C{str} 273 @param xdmcp_server: XDMCP server to connect to 274 @type xdmcp_server: C{str} 275 @param convert_encoding: convert file system encodings between server and client (for client-side shared folders) 276 @type convert_encoding: C{bool} 277 @param server_encoding: server-side file system / session encoding 278 @type server_encoding: C{str} 279 @param client_encoding: client-side file system encoding (if client-side is MS Windows, this parameter gets overwritten to WINDOWS-1252) 280 @type client_encoding: C{str} 281 @param rootdir: X2Go session directory, normally C{~/.x2go} 282 @type rootdir: C{str} 283 @param profile_name: the session profile name for this terminal session 284 @type profile_name: C{str} 285 @param profile_id: the session profile ID for this terminal session 286 @type profile_id: C{str} 287 @param print_action: either a print action short name (PDFVIEW, PDFSAVE, PRINT, PRINTCMD) or the 288 resp. C{X2goPrintActionXXX} class (where XXX equals one of the given short names) 289 @type print_action: C{str} or C{class} 290 @param print_action_args: optional arguments for a given print_action (for further info refer to 291 L{X2goPrintActionPDFVIEW}, L{X2goPrintActionPDFSAVE}, L{X2goPrintActionPRINT} and L{X2goPrintActionPRINTCMD}) 292 @type print_action_args: dict 293 @param info_backend: backend for handling storage of server session information 294 @type info_backend: C{X2goServerSessionInfo*} instance 295 @param list_backend: backend for handling storage of session list information 296 @type list_backend: C{X2goServerSessionList*} instance 297 @param proxy_backend: backend for handling the X-proxy connections 298 @type proxy_backend: C{X2goProxy*} instance 299 @param proxy_options: a set of very C{X2goProxy*} backend specific options; any option that is not known 300 to the C{X2goProxy*} backend will simply be ignored 301 @type proxy_options: C{dict} 302 @param client_rootdir: client base dir (default: ~/.x2goclient) 303 @type client_rootdir: C{str} 304 @param sessions_rootdir: sessions base dir (default: ~/.x2go) 305 @type sessions_rootdir: C{str} 306 @param session_instance: the L{X2goSession} instance that is parent to this terminal session 307 @type session_instance: C{obj} 308 @param logger: you can pass an L{X2goLogger} object to the 309 L{X2goTerminalSessionSTDOUT} constructor 310 @type logger: L{X2goLogger} instance 311 @param loglevel: if no L{X2goLogger} object has been supplied a new one will be 312 constructed with the given loglevel 313 @type loglevel: C{int} 314 315 """ 316 self.proxy = None 317 self.proxy_subprocess = None 318 self.proxy_options = proxy_options 319 320 self.active_threads = [] 321 self.reverse_tunnels = {} 322 323 self.print_queue = None 324 self.mimebox_queue = None 325 326 if logger is None: 327 self.logger = log.X2goLogger(loglevel=loglevel) 328 else: 329 self.logger = copy.deepcopy(logger) 330 self.logger.tag = __NAME__ 331 332 self.control_session = control_session 333 self.reverse_tunnels = self.control_session.get_transport().reverse_tunnels 334 335 self.client_rootdir = client_rootdir 336 self.sessions_rootdir = sessions_rootdir 337 338 self.params = X2goSessionParams() 339 340 self.params.geometry = str(geometry) 341 self.params.link = str(link) 342 self.params.pack = str(pack) 343 self.params.dpi = str(dpi) 344 self.params.cache_type = str(cache_type) 345 self.params.session_type = str(session_type) 346 self.params.kbtype = str(kbtype) 347 self.params.kblayout = str(kblayout) 348 self.params.kbvariant = str(kbvariant) 349 self.params.snd_system = str(snd_system) 350 self.params.cmd = str(cmd) 351 self.params.depth = str(depth) 352 353 self.params.published_applications = published_applications 354 self.published_applications = published_applications 355 356 self.params.rdp_server = str(rdp_server) 357 self.params.rdp_options = str(rdp_options) 358 self.params.xdmcp_server = str(xdmcp_server) 359 360 self.params.convert_encoding = convert_encoding 361 self.params.client_encoding = str(client_encoding) 362 self.params.server_encoding = str(server_encoding) 363 364 self.params.rootdir = (type(rootdir) is types.StringType) and rootdir or self.sessions_rootdir 365 self.params.update() 366 367 self.profile_name = profile_name 368 self.set_session_title = set_session_title 369 self.session_title = session_title 370 self.session_window = None 371 self.proxy_backend = proxy_backend 372 373 self.snd_port = snd_port 374 self.print_action = print_action 375 self.print_action_args = print_action_args 376 self.printing_backend = printing_backend 377 self.session_instance = session_instance 378 if self.session_instance: 379 self.client_instance = self.session_instance.client_instance 380 else: 381 self.client_instance = None 382 383 self._share_local_folder_busy = False 384 self._mk_sessions_rootdir(self.params.rootdir) 385 386 self.session_info = session_info 387 if self.session_info is not None: 388 if self.session_info.name: 389 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name) 390 else: 391 raise x2go_exceptions.X2goTerminalSessionException('no valid session info availble') 392 else: 393 self.session_info = info_backend() 394 395 self._share_local_folder_lock = threading.Lock() 396 self._cleaned_up = False
397
398 - def __del__(self):
399 """\ 400 Tidy up if terminal session gets destructed. 401 402 """ 403 self._x2go_tidy_up()
404
405 - def _x2go_tidy_up(self):
406 """\ 407 Tidy up this terminal session... 408 - shutdown all forwarding and reverse forwarding tunnels 409 - shutdown the print queue (if running) 410 - shutdown the MIME box queue (if running) 411 - clear the session info 412 413 """ 414 self.release_proxy() 415 416 try: 417 418 if self.control_session.get_transport() is not None: 419 try: 420 for _tunnel in [ _tun[1] for _tun in self.reverse_tunnels[self.session_info.name].values() ]: 421 if _tunnel is not None: 422 _tunnel.__del__() 423 except KeyError: 424 pass 425 426 if self.print_queue is not None: 427 self.print_queue.__del__() 428 429 if self.mimebox_queue is not None: 430 self.mimebox_queue.__del__() 431 432 except AttributeError: 433 pass 434 435 self.session_info.clear()
436
437 - def _mk_sessions_rootdir(self, rootdir):
438 """\ 439 Create the server-side session root dir (normally ~/.x2go). 440 441 @param rootdir: server-side session root directory 442 @type rootdir: C{str} 443 444 """ 445 try: 446 os.makedirs(rootdir) 447 except OSError, e: 448 if e.errno == 17: 449 # file exists 450 pass 451 else: 452 raise OSError, e
453
454 - def _rm_session_dirtree(self):
455 """\ 456 Purge client-side session dir (session cache directory). 457 458 """ 459 if self.session_info.name: 460 shutil.rmtree('%s/S-%s' % (self.params.rootdir, self.session_info), ignore_errors=True)
461
462 - def _rm_desktop_dirtree(self):
463 """\ 464 Purge client-side session dir (C-<display> directory) 465 466 """ 467 if self.session_info.display: 468 shutil.rmtree('%s/S-%s' % (self.params.rootdir, self.session_info.display), ignore_errors=True)
469
470 - def get_session_name(self):
471 """\ 472 Retrieve the X2Go session's name from the session info object. 473 474 @return: the session name 475 @rtype: C{str} 476 477 """ 478 return self.session_info.name
479
480 - def get_session_info(self):
481 """\ 482 Retrieve the X2Go session's session info object. 483 484 @return: the session info object 485 @rtype: C{X2goServerSessionInfo*} 486 487 """ 488 return self.session_info
489
490 - def get_session_cmd(self):
491 """\ 492 Retrieve the X2Go session's command as stored in the session parameter object. 493 494 @return: the session command 495 @rtype: C{str} 496 497 """ 498 return self.params.cmd
499
500 - def get_session_type(self):
501 """\ 502 Retrieve the X2Go session's session type as stored in the session parameter object. 503 504 @return: the session type 505 @rtype: C{str} 506 507 """ 508 return self.params.session_type
509
510 - def start_sound(self):
511 """\ 512 Initialize Paramiko/SSH reverse forwarding tunnel for X2Go sound. 513 514 Currently supported audio protocols: 515 516 - PulseAudio 517 - Esound (not tested very much) 518 519 @raise X2goControlSessionException: if the control session of this terminal session is not connected 520 521 """ 522 _tunnel = None 523 if self.reverse_tunnels[self.session_info.name]['snd'][1] is None: 524 if self.params.snd_system == 'pulse': 525 self.logger('initializing PulseAudio sound support in X2Go session', loglevel=log.loglevel_INFO) 526 ### 527 ### PULSEAUDIO 528 ### 529 if os.path.exists(os.path.normpath('%s/.pulse-cookie' % _LOCAL_HOME)): 530 # setup pulse client config file on X2Go server 531 cmd_line = "echo 'default-server=127.0.0.1:%s'>%s/.pulse-client.conf;" % (self.session_info.snd_port, self.session_info.remote_container) + \ 532 "echo 'cookie-file=%s/.pulse-cookie'>>%s/.pulse-client.conf" % (self.session_info.remote_container, self.session_info.remote_container) 533 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line) 534 535 self.control_session._x2go_sftp_put(local_path='%s/.pulse-cookie' % _LOCAL_HOME, remote_path='%s/.pulse-cookie' % self.session_info.remote_container) 536 537 # start reverse SSH tunnel for pulse stream 538 _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port, 539 remote_host='127.0.0.1', 540 remote_port=self.snd_port, 541 ssh_transport=self.control_session.get_transport(), 542 session_instance=self.session_instance, 543 logger=self.logger 544 ) 545 else: 546 if self.client_instance: 547 self.client_instance.HOOK_on_sound_tunnel_failed(profile_name=self.profile_name, session_name=self.session_info.name) 548 elif self.params.snd_system == 'arts': 549 ### 550 ### ARTSD AUDIO 551 ### 552 self.logger('the ArtsD sound server (as in KDE3) is obsolete and will not be supported by Python X2Go...', loglevel=log.loglevel_WARNING) 553 554 elif self.params.snd_system == 'esd': 555 ### 556 ### ESD AUDIO 557 ### 558 559 self.logger('initializing ESD sound support in X2Go session', loglevel=log.loglevel_INFO) 560 self.control_session._x2go_sftp_put(local_path='%s/.esd_auth' % _LOCAL_HOME, remote_path='%s/.esd_auth' % self.control_session._x2go_remote_home) 561 562 # start reverse SSH tunnel for pulse stream 563 _tunnel = rforward.X2goRevFwTunnel(server_port=self.session_info.snd_port, 564 remote_host='127.0.0.1', 565 remote_port=self.snd_port, 566 ssh_transport=self.control_session.get_transport(), 567 session_instance=self.session_instance, 568 logger=self.logger 569 ) 570 571 572 if _tunnel is not None: 573 self.reverse_tunnels[self.session_info.name]['snd'] = (self.session_info.snd_port, _tunnel) 574 _tunnel.start() 575 self.active_threads.append(_tunnel) 576 577 else: 578 # tunnel has already been started and might simply need a resume call 579 self.reverse_tunnels[self.session_info.name]['snd'][1].resume()
580
581 - def start_sshfs(self):
582 """\ 583 Initialize Paramiko/SSH reverse forwarding tunnel for X2Go folder sharing. 584 585 """ 586 if not self.control_session.is_sshfs_available(): 587 raise x2go_exceptions.X2goUserException('Remote user %s is not allowed to share SSHFS resources with the server.' % self.session_info.username) 588 589 # start reverse SSH tunnel for sshfs (folder sharing, printing) 590 ssh_transport = self.control_session.get_transport() 591 if self.reverse_tunnels[self.session_info.name]['sshfs'][1] is None: 592 593 _tunnel = sftpserver.X2goRevFwTunnelToSFTP(server_port=self.session_info.sshfs_port, 594 ssh_transport=ssh_transport, 595 auth_key=self.control_session._x2go_session_auth_rsakey, 596 session_instance=self.session_instance, 597 logger=self.logger 598 ) 599 600 if _tunnel is not None: 601 self.reverse_tunnels[self.session_info.name]['sshfs'] = (self.session_info.sshfs_port, _tunnel) 602 _tunnel.start() 603 self.active_threads.append(_tunnel) 604 while not _tunnel.ready: 605 gevent.sleep(.1) 606 607 else: 608 # tunnel has already been started and might simply need a resume call 609 self.reverse_tunnels[self.session_info.name]['sshfs'][1].resume()
610
611 - def _x2go_pause_rev_fw_tunnel(self, name):
612 """\ 613 Pause reverse SSH tunnel of name <name>. 614 615 @param name: tunnel name (either of C{sshfs}, C{snd}) 616 @type name: C{str} 617 618 """ 619 _tunnel = self.reverse_tunnels[self.session_info.name][name][1] 620 if _tunnel is not None: 621 _tunnel.pause()
622
623 - def stop_sound(self):
624 """\ 625 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2Go sound. 626 627 """ 628 self._x2go_pause_rev_fw_tunnel('snd')
629
630 - def stop_sshfs(self):
631 """\ 632 Shutdown (pause) Paramiko/SSH reverse forwarding tunnel for X2Go folder sharing. 633 634 """ 635 self._x2go_pause_rev_fw_tunnel('sshfs')
636
637 - def start_printing(self):
638 """\ 639 Initialize X2Go print spooling. 640 641 @raise X2goUserException: if the X2Go printing feature is not available to this user 642 643 """ 644 if not self.control_session.is_sshfs_available(): 645 raise x2go_exceptions.X2goUserException('Remote user %s is not allowed to use client-side printing.' % self.session_info.username) 646 647 spool_dir = os.path.join(self.session_info.local_container, 'spool') 648 if not os.path.exists(spool_dir): 649 os.makedirs(spool_dir) 650 self.share_local_folder(local_path=spool_dir, folder_type='spool') 651 self.print_queue = printqueue.X2goPrintQueue(profile_name=self.profile_name, 652 session_name=self.session_info.name, 653 spool_dir=spool_dir, 654 print_action=self.print_action, 655 print_action_args=self.print_action_args, 656 client_instance=self.client_instance, 657 printing_backend=self.printing_backend, 658 logger=self.logger, 659 ) 660 self.print_queue.start() 661 self.active_threads.append(self.print_queue)
662
663 - def set_print_action(self, print_action, **kwargs):
664 """\ 665 Set a print action for the next incoming print jobs. 666 667 This method is a wrapper for L{X2goPrintQueue}C{.set_print_action()}. 668 669 @param print_action: print action name or object (i.e. an instance of C{X2goPrintAction*} classes) 670 @type print_action: C{str} or C{X2goPrintAction*} 671 @param kwargs: print action specific parameters 672 @type kwargs: dict 673 674 """ 675 self.print_queue.set_print_action(print_action, logger=self.logger, **kwargs)
676
677 - def stop_printing(self):
678 """\ 679 Shutdown (pause) the X2Go Print Queue thread. 680 681 """ 682 if self.print_queue is not None: 683 self.print_queue.pause()
684
685 - def get_printing_spooldir(self):
686 """\ 687 Return the server-side printing spooldir path. 688 689 @return: the directory for remote print job spooling 690 @rtype: C{str} 691 692 """ 693 return '%s/%s' % (self.session_info.remote_container, 'spool')
694
695 - def start_mimebox(self, mimebox_extensions=[], mimebox_action=None):
696 """\ 697 Initialize the X2Go MIME box. Open/process incoming files from the server-side locally. 698 699 @param mimebox_extensions: file name extensions that are allowed for local opening/processing 700 @type mimebox_extensions: C{list} 701 @param mimebox_action: MIME box action given as name or object (i.e. an instance of C{X2goMIMEboxAction*} classes). 702 @type mimebox_action: C{str} or C{obj} 703 704 @raise X2goUserException: if the X2Go MIME box feature is not available to this user 705 706 """ 707 if not self.control_session.is_sshfs_available(): 708 raise x2go_exceptions.X2goUserException('Remote user %s is not allowed to use the MIME box.' % self.session_info.username) 709 710 mimebox_dir = os.path.join(self.session_info.local_container, 'mimebox') 711 if not os.path.exists(mimebox_dir): 712 os.makedirs(mimebox_dir) 713 self.share_local_folder(local_path=mimebox_dir, folder_type='mimebox') 714 self.mimebox_queue = mimebox.X2goMIMEboxQueue(profile_name=self.profile_name, 715 session_name=self.session_info.name, 716 mimebox_dir=mimebox_dir, 717 mimebox_extensions=mimebox_extensions, 718 mimebox_action=mimebox_action, 719 client_instance=self.client_instance, 720 logger=self.logger, 721 ) 722 self.mimebox_queue.start() 723 self.active_threads.append(self.mimebox_queue)
724
725 - def set_mimebox_action(self, mimebox_action, **kwargs):
726 """\ 727 Set a MIME box action for the next incoming MIME jobs. 728 729 This method is a wrapper for L{X2goMIMEboxQueue}C{set_mimebox_action()}. 730 731 @param mimebox_action: MIME box action name or object (i.e. an instance of C{X2goMIMEboxAction*} classes) 732 @type mimebox_action: C{str} or C{X2goMIMEboxAction*} 733 @param kwargs: MIME box action specific parameters 734 @type kwargs: dict 735 736 """ 737 self.mimebox_queue.set_mimebox_action(mimebox_action, logger=self.logger, **kwargs)
738
739 - def stop_mimebox(self):
740 """\ 741 Shutdown (pause) the X2Go MIME box Queue thread. 742 743 """ 744 if self.mimebox_queue is not None: 745 self.mimebox_queue.pause()
746
747 - def get_mimebox_spooldir(self):
748 """\ 749 Return the server-side MIME box spooldir path. 750 751 @return: the directory where remote MIME box jobs are placed 752 @rtype: C{str} 753 754 """ 755 return '%s/%s' % (self.session_info.remote_container, 'mimebox')
756
757 - def is_session_info_protected(self):
758 """\ 759 Test if this terminal's session info object is write-protected. 760 761 @return: C{True}, if session info object is read-only, C{False} for read-write. 762 @rtype: C{bool} 763 764 """ 765 self.session_info.is_protected()
766
767 - def session_info_protect(self):
768 """\ 769 Protect this terminal session's info object against updates. 770 771 """ 772 self.session_info.protect()
773
774 - def session_info_unprotect(self):
775 """\ 776 Allow session info updates from within the list_sessions method of the control session. 777 778 """ 779 self.session_info.unprotect()
780
781 - def share_local_folder(self, local_path=None, folder_type='disk'):
782 """\ 783 Share a local folder with the X2Go session. 784 785 @param local_path: the full path to an existing folder on the local 786 file system 787 @type local_path: C{str} 788 @param folder_type: one of 'disk' (a folder on your local hard drive), 'rm' (removeable device), 789 'cdrom' (CD/DVD Rom) or 'spool' (for X2Go print spooling) 790 @type folder_type: C{str} 791 792 @return: returns C{True} if the local folder has been successfully mounted within the X2Go server session 793 @rtype: C{bool} 794 795 @raise X2goUserException: if local folder sharing is not available to this user 796 @raise Exception: any other exception occuring on the way is passed through by this method 797 798 """ 799 if not self.control_session.is_sshfs_available(): 800 raise x2go_exceptions.X2goUserException('Remote user %s is not allowed to share local folders with the server.' % self.session_info.username) 801 802 if local_path is None: 803 self.logger('no folder name given...', log.loglevel_WARN) 804 return False 805 806 if type(local_path) not in (types.StringType, types.UnicodeType): 807 self.logger('folder name needs to be of type StringType...', log.loglevel_WARN) 808 return False 809 810 if not os.path.exists(local_path): 811 self.logger('local folder does not exist: %s' % local_path, log.loglevel_WARN) 812 return False 813 814 local_path = os.path.normpath(local_path) 815 self.logger('sharing local folder: %s' % local_path, log.loglevel_INFO) 816 817 _auth_rsakey = self.control_session._x2go_session_auth_rsakey 818 _host_rsakey = defaults.RSAHostKey 819 820 _tmp_io_object = cStringIO.StringIO() 821 _auth_rsakey.write_private_key(_tmp_io_object) 822 _tmp_io_object.write('----BEGIN RSA IDENTITY----') 823 _tmp_io_object.write('%s %s' % (_host_rsakey.get_name(),_host_rsakey.get_base64(),)) 824 825 # _x2go_key_fname must be a UniX path 826 _x2go_key_fname = '%s/%s/%s' % (os.path.dirname(self.session_info.remote_container), 'ssh', 'key.z%s' % self.session_info.agent_pid) 827 _x2go_key_bundle = _tmp_io_object.getvalue() 828 829 # if there is another call to this method currently being processed, wait for that one to finish 830 self._share_local_folder_lock.acquire() 831 832 try: 833 self.control_session._x2go_sftp_write(_x2go_key_fname, _x2go_key_bundle) 834 835 _convert_encoding = self.params.convert_encoding 836 _client_encoding = self.params.client_encoding 837 _server_encoding = self.params.server_encoding 838 839 if _X2GOCLIENT_OS == 'Windows': 840 local_path = local_path.replace('\\', '/') 841 local_path = local_path.replace(':', '') 842 local_path = '/windrive/%s' % local_path 843 _convert_encoding = True 844 _client_encoding = 'WINDOWS-1252' 845 846 if _convert_encoding: 847 export_iconv_settings = 'export X2GO_ICONV=modules=iconv,from_code=%s,to_code=%s && ' % (_client_encoding, _server_encoding) 848 else: 849 export_iconv_settings = '' 850 851 if folder_type == 'disk': 852 853 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings, 854 'x2gomountdirs', 855 'dir', 856 str(self.session_info.name), 857 '\'%s\'' % _CURRENT_LOCAL_USER, 858 _x2go_key_fname, 859 '%s__REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port), 860 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 861 ] 862 863 elif folder_type == 'spool': 864 865 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings, 866 'x2gomountdirs', 867 'dir', 868 str(self.session_info.name), 869 '\'%s\'' % _CURRENT_LOCAL_USER, 870 _x2go_key_fname, 871 '%s__PRINT_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port), 872 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 873 ] 874 875 elif folder_type == 'mimebox': 876 877 cmd_line = [ '%sexport HOSTNAME &&' % export_iconv_settings, 878 'x2gomountdirs', 879 'dir', 880 str(self.session_info.name), 881 '\'%s\'' % _CURRENT_LOCAL_USER, 882 _x2go_key_fname, 883 '%s__MIMEBOX_SPOOL___REVERSESSH_PORT__%s; ' % (local_path, self.session_info.sshfs_port), 884 'rm -f %s %s.ident' % (_x2go_key_fname, _x2go_key_fname), 885 ] 886 887 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line) 888 _stdout = stdout.read().split('\n') 889 self.logger('x2gomountdirs output is: %s' % _stdout, log.loglevel_NOTICE) 890 891 except: 892 self._share_local_folder_lock.release() 893 raise 894 self._share_local_folder_lock.release() 895 896 if len(_stdout) >= 6 and _stdout[5].endswith('ok'): 897 return True 898 return False
899
900 - def unshare_all_local_folders(self):
901 """\ 902 Unshare all local folders mount in the X2Go session. 903 904 @return: returns C{True} if all local folders could be successfully unmounted from the X2Go server session 905 @rtype: C{bool} 906 907 """ 908 self.logger('unsharing all local folders from session %s' % self.session_info, log.loglevel_INFO) 909 910 cmd_line = [ 'export HOSTNAME &&', 911 'x2goumount-session', 912 self.session_info.name, 913 ] 914 915 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line) 916 if not stderr.read(): 917 self.logger('x2goumount-session (all mounts) for session %s has been successful' % self.session_info, log.loglevel_NOTICE) 918 return True 919 else: 920 self.logger('x2goumount-session (all mounts) for session %s failed' % self.session_info, log.loglevel_ERROR) 921 return False
922
923 - def unshare_local_folder(self, local_path):
924 """\ 925 Unshare local folder given as <local_path> from X2Go session. 926 927 @return: returns C{True} if the local folder <local_path> could be successfully unmounted from the X2Go server session 928 @rtype: C{bool} 929 930 """ 931 self.logger('unsharing local folder from session %s' % self.session_info, log.loglevel_INFO) 932 933 cmd_line = [ 'export HOSTNAME &&', 934 'x2goumount-session', 935 self.session_info.name, 936 "'%s'" % local_path, 937 ] 938 939 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line) 940 if not stderr.read(): 941 self.logger('x2goumount-session (%s) for session %s has been successful' % (local_path, self.session_info, ), log.loglevel_NOTICE) 942 return True 943 else: 944 self.logger('x2goumount-session (%s) for session %s failed' % (local_path, self.session_info, ), log.loglevel_ERROR) 945 return False
946
947 - def color_depth(self):
948 """\ 949 Retrieve the session's color depth. 950 951 @return: the session's color depth 952 @rtype: C{int} 953 954 """ 955 return self.params.depth
956
957 - def auto_session_window_title(self, dont_set=False):
958 """\ 959 Automatically generate an appropriate human-readable session window title. 960 961 The session window title will be provider in the C{session_title} property of 962 this method. 963 964 @param dont_set: generate the session window title, but do not actually set it 965 @type dont_set: C{bool} 966 967 """ 968 _generic_title = 'X2GO-%s' % self.session_info.name 969 970 # no blanks at beginning or end, no blanks-only... 971 self.session_title = self.session_title.strip() 972 973 if self.params.session_type == 'D': 974 if self.set_session_title: 975 976 if not self.session_title: 977 self.session_title = '%s for %s@%s' % (self.params.cmd, self.control_session.remote_username(), self.control_session.get_hostname()) 978 979 else: 980 # session title fallback... (like X2Go server does it...) 981 self.session_title = _generic_title 982 983 else: 984 # do nothing for rootless sessions 985 self.session_title = _generic_title 986 987 if self.session_title != _generic_title and not dont_set: 988 self.set_session_window_title(title=self.session_title)
989
990 - def find_session_window(self, timeout=60):
991 """\ 992 Try for <timeout> seconds to find the X2Go session window of this 993 terminal session. 994 995 A background thread will get spawned for this operation. 996 997 @param timeout: try for <timeout> seconds to find the session window 998 @type timeout: C{int} 999 1000 """ 1001 gevent.spawn(self._find_session_window, timeout=timeout)
1002
1003 - def _find_session_window(self, timeout=0):
1004 """\ 1005 Try for <timeout> seconds to find the X2Go session window of this 1006 terminal session. 1007 1008 @param timeout: try for <timeout> seconds to find the session window 1009 @type timeout: C{int} 1010 1011 """ 1012 self.session_window = None 1013 1014 # search for the window of our focus, do this in a loop till the window as been found 1015 # or timeout forces us to give up... 1016 timeout += 1 1017 while timeout: 1018 1019 timeout -= 1 1020 1021 window = utils.find_session_window(self.session_info.name) 1022 1023 if window is not None: 1024 self.session_window = window 1025 break 1026 1027 gevent.sleep(1)
1028
1029 - def set_session_window_title(self, title, timeout=60):
1030 """\ 1031 Modify the session window title. 1032 1033 A background thread will get spawned for this operation. 1034 1035 @param title: new title for the terminal session's session window 1036 @type title: C{str} 1037 @param timeout: try for <timeout> seconds to find the session window 1038 @type timeout: C{int} 1039 1040 """ 1041 gevent.spawn(self._set_session_window_title, title=title.strip(), timeout=timeout)
1042
1043 - def _set_session_window_title(self, title, timeout=0):
1044 """\ 1045 Modify the session window title. 1046 1047 @param title: new title for the terminal session's session window 1048 @type title: C{str} 1049 @param timeout: try for <timeout> seconds to find the session window 1050 @type timeout: C{int} 1051 1052 """ 1053 self.session_title = title 1054 1055 if not self.session_title: 1056 self.auto_session_title(dont_set=True) 1057 1058 timeout += 1 1059 while timeout: 1060 1061 timeout -= 1 1062 1063 if self.session_window is not None: 1064 utils.set_session_window_title(self.session_window, self.session_title) 1065 break 1066 1067 gevent.sleep(1)
1068
1069 - def raise_session_window(self, timeout=60):
1070 """\ 1071 Try for <timeout> seconds to raise the X2Go session window of this 1072 terminal session to the top and bring it to focus. 1073 1074 A background thread will get spawned for this operation. 1075 1076 @param timeout: try for <timeout> seconds to raise the session window 1077 @type timeout: C{int} 1078 1079 """ 1080 gevent.spawn(self._raise_session_window, timeout=timeout)
1081
1082 - def _raise_session_window(self, timeout=0):
1083 """ 1084 Try for <timeout> seconds to raise the X2Go session window of this 1085 terminal session to the top and bring it to focus. 1086 1087 @param timeout: try for <timeout> seconds to raise the session window 1088 @type timeout: C{int} 1089 1090 """ 1091 timeout += 1 1092 while timeout: 1093 1094 timeout -= 1 1095 1096 if self.session_window is not None: 1097 1098 utils.raise_session_window(self.session_window) 1099 break 1100 1101 gevent.sleep(1)
1102
1103 - def has_command(self, cmd):
1104 """\ 1105 ,,Guess'' if the command C{<cmd>} exists on the X2Go server and is executable. 1106 The expected result is not 100% safe, however, it comes with a high probability to 1107 be correct. 1108 1109 @param cmd: session command 1110 @type cmd: C{str} 1111 1112 @return: C{True} if this method reckons that the command is executable on the remote X2Go server 1113 @rtype: C{bool} 1114 1115 """ 1116 test_cmd = None; 1117 1118 cmd = cmd.strip('"').strip('"') 1119 if cmd.find('RDP') != -1: 1120 cmd = 'rdesktop' 1121 1122 if cmd in _X2GO_GENERIC_APPLICATIONS: 1123 return True 1124 if cmd in _X2GO_DESKTOPSESSIONS.keys(): 1125 return True 1126 elif 'XSHAD' in cmd: 1127 return True 1128 elif 'PUBLISHED' in cmd and 'X2GO_PUBLISHED_APPLICATIONS' in self.control_session.get_server_features(): 1129 return True 1130 elif cmd and cmd.startswith('/'): 1131 # check if full path is correct _and_ if application is in server path 1132 test_cmd = 'test -x %s && which %s && echo OK' % (cmd, os.path.basename(cmd.split()[0])) 1133 elif cmd and '/' not in cmd.split()[0]: 1134 # check if application is in server path only 1135 test_cmd = 'which %s && echo OK' % os.path.basename(cmd.split()[0]) 1136 1137 if test_cmd: 1138 (stdin, stdout, stderr) = self.control_session._x2go_exec_command([test_cmd]) 1139 _stdout = stdout.read() 1140 return _stdout.find('OK') != -1 1141 else: 1142 return False
1143
1144 - def run_command(self, cmd=None, env={}):
1145 """\ 1146 Run a command in this session. 1147 1148 After L{X2goTerminalSessionSTDOUT.start()} has been called 1149 one or more commands can be executed with L{X2goTerminalSessionSTDOUT.run_command()} 1150 within the current X2Go session. 1151 1152 @param cmd: Command to be run 1153 @type cmd: C{str} 1154 @param env: add server-side environment variables 1155 @type env: C{dict} 1156 1157 @return: stdout.read() and stderr.read() as returned by the run command 1158 on the X2Go server 1159 @rtype: C{tuple} of C{str} 1160 1161 """ 1162 if not self.has_command(_rewrite_cmd(str(self.params.cmd), params=self.params)): 1163 if self.client_instance: 1164 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd) 1165 return False 1166 1167 if cmd in ("", None): 1168 if self.params.cmd is None: 1169 cmd = 'TERMINAL' 1170 else: 1171 cmd = self.params.cmd 1172 1173 if cmd == 'XDMCP': 1174 # do not run command when in XDMCP mode... 1175 return None 1176 1177 if 'XSHAD' in cmd: 1178 # do not run command when in DESKTOP SHARING mode... 1179 return None 1180 1181 self.params.update(cmd=cmd) 1182 1183 # do not allow the execution of full path names 1184 if '/' in cmd: 1185 cmd = os.path.basename(cmd) 1186 1187 cmd_line = [ "setsid x2goruncommand", 1188 str(self.session_info.display), 1189 str(self.session_info.agent_pid), 1190 str(self.session_info.name), 1191 str(self.session_info.snd_port), 1192 _rewrite_blanks(_rewrite_cmd(cmd, params=self.params)), 1193 str(self.params.snd_system), 1194 str(self.params.session_type), 1195 "1>/dev/null 2>/dev/null & exit", 1196 ] 1197 1198 if self.params.snd_system == 'pulse': 1199 cmd_line = [ 'PULSE_CLIENTCONFIG=%s/.pulse-client.conf' % self.session_info.remote_container ] + cmd_line 1200 1201 if env: 1202 for env_var in env.keys(): 1203 cmd_line = [ '%s=%s' % (env_var, env[env_var]) ] + cmd_line 1204 1205 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line) 1206 1207 if self.params.kbtype not in ('null/null', 'auto') and (self.params.kblayout not in ('null', '') or self.params.kbvariant not in ('null', '')): 1208 self.set_keyboard(layout=self.params.kblayout, variant=self.params.kbvariant) 1209 1210 return stdout.read(), stderr.read()
1211
1212 - def is_desktop_session(self):
1213 """\ 1214 Is this (terminal) session a desktop session? 1215 1216 @return: Returns C{True} is this session is a desktop session. 1217 @rtype: C{bool} 1218 1219 """ 1220 if self.session_info: 1221 return self.session_info.is_desktop_session() 1222 return False
1223
1225 """\ 1226 Is this (terminal) session a published applications provider? 1227 1228 @return: Returns C{True} is this session is a provider session for published applications. 1229 @rtype: C{bool} 1230 1231 """ 1232 if self.session_info and self.is_running(): 1233 return self.session_info.is_published_applications_provider() 1234 return False
1235
1236 - def set_keyboard(self, layout='null', variant='null'):
1237 """\ 1238 Set the keyboard layout and variant for this (running) session. 1239 1240 @param layout: keyboard layout to be set 1241 @type layout: C{str} 1242 @param variant: keyboard variant to be set 1243 @type variant: C{str} 1244 1245 @return: returns C{True} if the {setxkbmap} command could be executed successfully. 1246 @rtype: C{bool} 1247 1248 """ 1249 if not self.is_running(): 1250 return False 1251 1252 cmd_line = [ 'export DISPLAY=:%s && ' % str(self.session_info.display), 1253 'setxkbmap ' 1254 ] 1255 1256 if layout != 'null': 1257 self.logger('setting keyboad layout ,,%s\'\' for session %s' % (layout, self.session_info), log.loglevel_INFO) 1258 cmd_line.append('-layout %s' % layout) 1259 if variant != 'null': 1260 self.logger('setting keyboad variant ,,%s\'\' for session %s' % (variant, self.session_info), log.loglevel_INFO) 1261 cmd_line.append('-variant %s' % variant) 1262 1263 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line) 1264 _stderr = stderr.read() 1265 if not _stderr: 1266 self.logger('setting keyboard layout ,,%s\'\' and variant ,,%s\'\' for session %s has been successful' % (layout, variant, self.session_info), log.loglevel_NOTICE) 1267 return True 1268 else: 1269 self.logger('setting keyboard layout ,,%s\'\' and variant ,,%s\'\' for session %s failed: %s' % (layout, variant, self.session_info, _stderr.replace('\n', ' ')), log.loglevel_ERROR) 1270 return False
1271
1272 - def exec_published_application(self, exec_name, timeout=20, env={}):
1273 """\ 1274 Executed a published application. 1275 1276 @param exec_name: application to be executed 1277 @type exec_name: C{str} 1278 @param timeout: execution timeout 1279 @type timeout: C{int} 1280 @param env: session environment dictionary 1281 @type env: C{dict} 1282 1283 """ 1284 cmd_line = [ 1285 "export DISPLAY=:%s && " % str(self.session_info.display), 1286 "export X2GO_SESSION=%s && " % str(self.get_session_name()), 1287 ] 1288 1289 if self.params.snd_system == 'pulse': 1290 cmd_line.append("export PULSE_CLIENTCONFIG=%s/.pulse-client.conf && " % self.session_info.remote_container) 1291 1292 if env: 1293 for env_var in env.keys(): 1294 cmd_line = [ 'export %s=%s && ' % (env_var, env[env_var]) ] + cmd_line 1295 1296 cmd_line.extend( 1297 [ 1298 "setsid %s" % exec_name, 1299 "1>/dev/null 2>/dev/null & exit", 1300 ] 1301 ) 1302 1303 self.logger('executing published application %s for %s with command line: %s' % (exec_name, self.profile_name, cmd_line), loglevel=log.loglevel_DEBUG) 1304 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line, timeout=timeout)
1305
1306 - def ok(self):
1307 """\ 1308 X2Go session OK? 1309 1310 @return: Returns C{True} if this X2Go (terminal) session is up and running, 1311 C{False} otherwise. 1312 @rtype: C{bool} 1313 1314 """ 1315 _ok = bool(self.session_info.name and self.proxy.ok()) 1316 return _ok
1317
1318 - def is_running(self):
1319 """\ 1320 X2Go session running? 1321 1322 @return: Returns C{True} if this X2Go (terminal) session is in running state, 1323 C{False} otherwise. 1324 @rtype: C{bool} 1325 1326 """ 1327 return self.session_info.is_running()
1328
1329 - def is_suspended(self):
1330 """\ 1331 X2Go session suspended? 1332 1333 @return: Returns C{True} if this X2Go (terminal) session is in suspended state, 1334 C{False} otherwise. 1335 @rtype: C{bool} 1336 1337 """ 1338 return self.session_info.is_suspended()
1339
1340 - def is_connected(self):
1341 """\ 1342 X2Go session connected? 1343 1344 @return: Returns C{True} if this X2Go session's Paramiko/SSH transport is 1345 connected/authenticated, C{False} else. 1346 @rtype: C{bool} 1347 1348 """ 1349 return self.control_session.is_connected()
1350
1351 - def start(self):
1352 """\ 1353 Start a new X2Go session. 1354 1355 @return: C{True} if session startup has been successful and the X2Go proxy is up-and-running 1356 @rtype: C{bool} 1357 1358 @raise X2goTerminalSessionException: if the session startup failed 1359 1360 """ 1361 self.params.rewrite_session_type() 1362 1363 if not self.has_command(_rewrite_cmd(self.params.cmd, params=self.params)): 1364 if self.client_instance: 1365 self.client_instance.HOOK_no_such_command(profile_name=self.profile_name, session_name=self.session_info.name, cmd=self.params.cmd) 1366 return False 1367 1368 setkbd = "0" 1369 if self.params.kbtype != "null/null": 1370 setkbd = "1" 1371 1372 if '/' in self.params.cmd: 1373 self.params.cmd = os.path.basename(self.params.cmd) 1374 1375 self.params.rewrite_session_type() 1376 1377 if self.params.geometry == 'maximize': 1378 _geometry = utils.get_workarea_geometry() 1379 if _geometry is None or len(_geometry) != 2: 1380 _geometry = utils.get_desktop_geometry() 1381 if _geometry and len(_geometry) == 2: 1382 self.params.geometry = "%sx%s" % _geometry 1383 else: 1384 self.logger('failed to detect best maxmimized geometry of your client-side desktop', loglevel=log.loglevel_WARN) 1385 self.params.geometry = "1024x768" 1386 1387 cmd_line = [ "x2gostartagent", 1388 str(self.params.geometry), 1389 str(self.params.link), 1390 str(self.params.pack), 1391 str(self.params.cache_type+'-depth_'+self.params.depth), 1392 str(self.params.kblayout), 1393 str(self.params.kbtype), 1394 str(setkbd), 1395 str(self.params.session_type), 1396 str(self.params.cmd), 1397 ] 1398 1399 if self.params.cmd == 'XDMCP' and self.params.xdmcp_server: 1400 cmd_line = ['X2GOXDMCP=%s' % self.params.xdmcp_server] + cmd_line 1401 1402 if self.params.dpi: 1403 cmd_line = ['X2GODPI=%s' % self.params.dpi] + cmd_line 1404 1405 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line) 1406 1407 _stdout = stdout.read() 1408 _stderr = stderr.read() 1409 1410 # if the first line of stdout is a "DEN(Y)" string then we will presume that 1411 # we tried to use X2Go desktop sharing and the sharing was rejected 1412 if "ACCESS DENIED" in _stderr and "XSHAD" in _stderr: 1413 raise x2go_exceptions.X2goDesktopSharingException('X2Go desktop sharing has been denied by the remote user') 1414 1415 try: 1416 self.session_info.initialize(_stdout, 1417 username=self.control_session.remote_username(), 1418 hostname=self.control_session.remote_peername(), 1419 ) 1420 except ValueError: 1421 raise x2go_exceptions.X2goTerminalSessionException("failed to start X2Go session") 1422 except IndexError: 1423 raise x2go_exceptions.X2goTerminalSessionException("failed to start X2Go session") 1424 1425 # local path may be a Windows path, so we use the path separator of the local system 1426 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name) 1427 # remote path is always a UniX path... 1428 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home, 1429 self.session_info.name, 1430 ) 1431 1432 # set up SSH tunnel for X11 graphical elements 1433 self.proxy = self.proxy_backend(session_info=self.session_info, 1434 ssh_transport=self.control_session.get_transport(), 1435 sessions_rootdir=self.sessions_rootdir, 1436 session_instance=self.session_instance, 1437 proxy_options=self.proxy_options, 1438 logger=self.logger) 1439 self.proxy_subprocess, proxy_ok = self.proxy.start_proxy() 1440 1441 if proxy_ok: 1442 self.active_threads.append(self.proxy) 1443 1444 if self.params.session_type in ('D', 'S'): 1445 self.find_session_window() 1446 self.auto_session_window_title() 1447 self.raise_session_window() 1448 1449 if self.params.published_applications: 1450 self.control_session.get_published_applications() 1451 1452 else: 1453 raise x2go_exceptions.X2goTerminalSessionException("failed to start X2Go session") 1454 1455 return proxy_ok
1456
1457 - def resume(self):
1458 """\ 1459 Resume a running/suspended X2Go session. 1460 1461 @return: C{True} if the session could successfully be resumed 1462 @rtype: C{bool} 1463 1464 @raise X2goTerminalSessionException: if the terminal session failed to update server-side reported port changes 1465 1466 """ 1467 setkbd = "0" 1468 if self.params.kbtype != "null/null": 1469 setkbd = "1" 1470 1471 if self.params.geometry == 'maximize': 1472 _geometry = utils.get_workarea_geometry() 1473 if _geometry is None or len(_geometry) != 2: 1474 _geometry = utils.get_desktop_geometry() 1475 if _geometry and len(_geometry) == 2: 1476 self.params.geometry = "%sx%s" % _geometry 1477 else: 1478 self.logger('failed to detect best maxmimized geometry of your client-side desktop, using 1024x768 instead', loglevel=log.loglevel_WARN) 1479 self.params.geometry = "1024x768" 1480 1481 cmd_line = [ "x2goresume-session", self.session_info.name, 1482 self.params.geometry, 1483 self.params.link, 1484 self.params.pack, 1485 self.params.kblayout, 1486 self.params.kbtype, 1487 setkbd, 1488 ] 1489 1490 (stdin, stdout, stderr) = self.control_session._x2go_exec_command(cmd_line) 1491 1492 # re-allocate (if needed) server-side ports for graphics, sound and sshfs 1493 for stdout_line in stdout.read(): 1494 try: 1495 _new_value = stdout_line.split("=")[1].strip() 1496 if 'gr_port=' in stdout_line and _new_value != str(self.session_info.graphics_port): 1497 try: 1498 self.session_info.graphics_port = int(_new_value) 1499 self.logger('re-allocating graphics port for session %s, old server-side port is in use; new graphics port is %s' % (self.session_info, self.session_info.graphics_port), loglevel=log.loglevel_NOTICE) 1500 except TypeError: 1501 # if the re-allocation fails, this is fatal!!! 1502 raise x2go_exceptions.X2goTerminalSessionException('Failed to retrieve new graphics port from server. X2Go Session cannot be resumed.') 1503 elif 'sound_port=' in stdout_line and _new_value != str(self.session_info.snd_port): 1504 try: 1505 self.session_info.snd_port = int(_new_value) 1506 self.logger('re-allocating sound port for session %s, old server-side port is in use; new sound port is %s' % (self.session_info, self.session_info.snd_port), loglevel=log.loglevel_NOTICE) 1507 except TypeError: 1508 self.logger('Failed to retrieve new sound port from server for session %s, session will be without sound.' % self.session_info, loglevel=log.loglevel_WARN) 1509 elif 'fs_port=' in stdout_line and _new_value != str(self.session_info.sshfs_port): 1510 try: 1511 self.session_info.sshfs_port = int(_new_value) 1512 self.logger('re-allocating sshfs port for session %s, old server-side port is in use; new sshfs port is %s' % (self.session_info, self.session_info.sshfs_port), loglevel=log.loglevel_NOTICE) 1513 except TypeError: 1514 self.logger('Failed to retrieve new sshfs port from server for session %s, session will be without client-side folder sharing. Neither will there be X2Go printing nor X2Go MIME box support.' % self.session_info, loglevel=log.loglevel_WARN) 1515 except IndexError: 1516 continue 1517 1518 self.proxy = self.proxy_backend(session_info=self.session_info, 1519 ssh_transport=self.control_session.get_transport(), 1520 sessions_rootdir=self.sessions_rootdir, 1521 session_instance=self.session_instance, 1522 logger=self.logger 1523 ) 1524 self.proxy_subprocess, proxy_ok = self.proxy.start_proxy() 1525 1526 if proxy_ok: 1527 # local path may be a Windows path, so we use the path separator of the local system 1528 self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name) 1529 # remote path is always a UniX path... 1530 self.session_info.remote_container = '%s/.x2go/C-%s' % (self.control_session._x2go_remote_home, 1531 self.session_info.name, 1532 ) 1533 self.params.depth = self.session_info.name.split('_')[2][2:] 1534 1535 # on a session resume the user name comes in as a user ID. We have to translate this... 1536 self.session_info.username = self.control_session.remote_username() 1537 1538 if self.params.kbtype not in ('null/null', 'auto') and (self.params.kblayout not in ('null', '') or self.params.kbvariant not in ('null', '')): 1539 self.set_keyboard(layout=self.params.kblayout, variant=self.params.kbvariant) 1540 1541 if self.params.session_type in ('D', 'S'): 1542 self.find_session_window() 1543 self.auto_session_window_title() 1544 self.raise_session_window() 1545 1546 if self.is_published_applications_provider(): 1547 self.control_session.get_published_applications() 1548 self.published_applications = True 1549 else: 1550 raise x2go_exceptions.X2goTerminalSessionException("failed to start X2Go session") 1551 1552 return proxy_ok
1553
1554 - def suspend(self):
1555 """\ 1556 Suspend this X2Go (terminal) session. 1557 1558 @return: C{True} if the session terminal could be successfully suspended 1559 @rtype: C{bool} 1560 1561 """ 1562 self.control_session.suspend(session_name=self.session_info.name) 1563 self.release_proxy() 1564 1565 # TODO: check if session has really suspended 1566 _ret = True 1567 1568 return _ret
1569
1570 - def terminate(self):
1571 """\ 1572 Terminate this X2Go (terminal) session. 1573 1574 @return: C{True} if the session could be successfully terminated 1575 @rtype: C{bool} 1576 1577 """ 1578 self.control_session.terminate(session_name=self.session_info.name, destroy_terminals=False) 1579 self.release_proxy() 1580 self.post_terminate_cleanup() 1581 self.__del__() 1582 1583 # TODO: check if session has really suspended 1584 _ret = True 1585 1586 return _ret
1587
1588 - def release_proxy(self):
1589 """\ 1590 Let the X2Go proxy command cleanly die away... (by calling its destructor). 1591 1592 """ 1593 if self.proxy is not None: 1594 self.proxy.__del__()
1595
1596 - def post_terminate_cleanup(self):
1597 """\ 1598 Do some cleanup after this session has terminated. 1599 1600 """ 1601 # this method might be called twice (directly and from update_status in the session 1602 # registry instance. So we have to make sure, that this code will not fail 1603 # if called twice. 1604 if not self._cleaned_up and self.session_info.name: 1605 1606 # otherwise we wipe the session files locally 1607 self.logger('cleaning up session %s after termination' % self.session_info, loglevel=log.loglevel_NOTICE) 1608 1609 # if we run in debug mode, we keep local session directories 1610 if self.logger.get_loglevel() & log.loglevel_DEBUG != log.loglevel_DEBUG: 1611 1612 self._rm_session_dirtree() 1613 self._rm_desktop_dirtree() 1614 1615 self._cleaned_up = True
1616