Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2# This file is part of Xpra. 

3# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org> 

4# Copyright (C) 2008 Nathaniel Smith <njs@pobox.com> 

5# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 

6# later version. See the file COPYING for details. 

7 

8# DO NOT IMPORT GTK HERE: see 

9# http://lists.partiwm.org/pipermail/parti-discuss/2008-September/000041.html 

10# http://lists.partiwm.org/pipermail/parti-discuss/2008-September/000042.html 

11# (also do not import anything that imports gtk) 

12import sys 

13import os.path 

14import atexit 

15import traceback 

16 

17from xpra.scripts.main import info, warn, no_gtk, validate_encryption, parse_env, configure_env 

18from xpra.scripts.config import InitException, FALSE_OPTIONS, parse_bool 

19from xpra.common import CLOBBER_USE_DISPLAY, CLOBBER_UPGRADE 

20from xpra.exit_codes import EXIT_VFB_ERROR, EXIT_OK 

21from xpra.os_util import ( 

22 SIGNAMES, POSIX, WIN32, OSX, 

23 FDChangeCaptureContext, 

24 force_quit, 

25 get_username_for_uid, get_home_for_uid, get_shell_for_uid, getuid, setuidgid, 

26 get_hex_uuid, get_status_output, strtobytes, bytestostr, get_util_logger, osexpand, 

27 ) 

28from xpra.util import envbool, unsetenv, noerr 

29from xpra.platform.dotxpra import DotXpra 

30 

31 

32_cleanups = [] 

33def run_cleanups(): 

34 global _cleanups 

35 cleanups = _cleanups 

36 _cleanups = [] 

37 for c in cleanups: 

38 try: 

39 c() 

40 except Exception: 

41 print("error running cleanup %s" % c) 

42 traceback.print_exception(*sys.exc_info()) 

43 

44_when_ready = [] 

45 

46def add_when_ready(f): 

47 _when_ready.append(f) 

48 

49def add_cleanup(f): 

50 _cleanups.append(f) 

51 

52 

53def deadly_signal(signum): 

54 info("got deadly signal %s, exiting\n" % SIGNAMES.get(signum, signum)) 

55 run_cleanups() 

56 # This works fine in tests, but for some reason if I use it here, then I 

57 # get bizarre behavior where the signal handler runs, and then I get a 

58 # KeyboardException (?!?), and the KeyboardException is handled normally 

59 # and exits the program (causing the cleanup handlers to be run again): 

60 #signal.signal(signum, signal.SIG_DFL) 

61 #kill(os.getpid(), signum) 

62 force_quit(128 + signum) 

63 

64 

65def _root_prop_set(prop_name, ptype="u32", value=0): 

66 from xpra.gtk_common.gtk_util import get_default_root_window 

67 from xpra.x11.gtk_x11.prop import prop_set 

68 prop_set(get_default_root_window(), prop_name, ptype, value) 

69def _root_prop_get(prop_name, ptype="u32"): 

70 from xpra.gtk_common.gtk_util import get_default_root_window 

71 from xpra.x11.gtk_x11.prop import prop_get 

72 try: 

73 return prop_get(get_default_root_window(), prop_name, ptype) 

74 except Exception: 

75 return None 

76 

77def _save_int(prop_name, pid): 

78 _root_prop_set(prop_name, "u32", pid) 

79 

80def _get_int(prop_name): 

81 return _root_prop_get(prop_name, "u32") 

82 

83def _save_str(prop_name, s): 

84 _root_prop_set(prop_name, "latin1", s) 

85 

86def _get_str(prop_name): 

87 v = _root_prop_get(prop_name, "latin1") 

88 if v is not None: 

89 return v.encode("latin1") 

90 return v 

91 

92 

93def save_xvfb_pid(pid): 

94 _save_int("_XPRA_SERVER_PID", pid) 

95 

96def get_xvfb_pid(): 

97 return _get_int("_XPRA_SERVER_PID") 

98 

99 

100def save_uinput_id(uuid): 

101 _save_str("_XPRA_UINPUT_ID", (uuid or b"").decode()) 

102 

103#def get_uinput_id(): 

104# return _get_str("_XPRA_UINPUT_ID") 

105 

106 

107def validate_pixel_depth(pixel_depth, starting_desktop=False): 

108 try: 

109 pixel_depth = int(pixel_depth) 

110 except ValueError: 

111 raise InitException("invalid value '%s' for pixel depth, must be a number" % pixel_depth) from None 

112 if pixel_depth==0: 

113 pixel_depth = 24 

114 if pixel_depth not in (8, 16, 24, 30): 

115 raise InitException("invalid pixel depth: %s" % pixel_depth) 

116 if not starting_desktop and pixel_depth==8: 

117 raise InitException("pixel depth 8 is only supported in 'start-desktop' mode") 

118 return pixel_depth 

119 

120 

121def display_name_check(display_name): 

122 """ displays a warning 

123 when a low display number is specified """ 

124 if not display_name.startswith(":"): 

125 return 

126 n = display_name[1:].split(".")[0] #ie: ":0.0" -> "0" 

127 try: 

128 dno = int(n) 

129 if 0<=dno<10: 

130 warn("WARNING: low display number: %s" % dno) 

131 warn(" You are attempting to run the xpra server") 

132 warn(" against a low X11 display number: '%s'." % (display_name,)) 

133 warn(" This is generally not what you want.") 

134 warn(" You should probably use a higher display number") 

135 warn(" just to avoid any confusion and this warning message.") 

136 except IOError: 

137 pass 

138 

139 

140def print_DE_warnings(): 

141 de = os.environ.get("XDG_SESSION_DESKTOP") or os.environ.get("SESSION_DESKTOP") 

142 if not de: 

143 return 

144 log = get_util_logger() 

145 log.warn("Warning: xpra start from an existing '%s' desktop session", de) 

146 log.warn(" without using dbus-launch,") 

147 log.warn(" notifications forwarding may not work") 

148 log.warn(" try using a clean environment, a dedicated user,") 

149 log.warn(" or disable xpra's notifications option") 

150 

151 

152def sanitize_env(): 

153 #we don't want client apps to think these mean anything: 

154 #(if set, they belong to the desktop the server was started from) 

155 #TODO: simply whitelisting the env would be safer/better 

156 unsetenv("DESKTOP_SESSION", 

157 "GDMSESSION", 

158 "GNOME_DESKTOP_SESSION_ID", 

159 "SESSION_MANAGER", 

160 "XDG_VTNR", 

161 #we must keep this value on Debian / Ubuntu 

162 #to avoid breaking menu loading: 

163 #"XDG_MENU_PREFIX", 

164 "XDG_CURRENT_DESKTOP", 

165 "XDG_SESSION_DESKTOP", 

166 "XDG_SESSION_TYPE", 

167 "XDG_SESSION_ID", 

168 "XDG_SEAT", 

169 "XDG_VTNR", 

170 "QT_GRAPHICSSYSTEM_CHECKED", 

171 "CKCON_TTY", 

172 "CKCON_X11_DISPLAY", 

173 "CKCON_X11_DISPLAY_DEVICE", 

174 "WAYLAND_DISPLAY", 

175 ) 

176 

177def configure_imsettings_env(input_method): 

178 im = (input_method or "").lower() 

179 if im in ("none", "no"): 

180 #the default: set DISABLE_IMSETTINGS=1, fallback to xim 

181 #that's because the 'ibus' 'immodule' breaks keyboard handling 

182 #unless its daemon is also running - and we don't know if it is.. 

183 imsettings_env(True, "xim", "xim", "xim", "none", "@im=none") 

184 elif im=="keep": 

185 #do nothing and keep whatever is already set, hoping for the best 

186 pass 

187 elif im in ("xim", "ibus", "scim", "uim"): 

188 #ie: (False, "ibus", "ibus", "IBus", "@im=ibus") 

189 imsettings_env(True, im.lower(), im.lower(), im.lower(), im, "@im=%s" % im.lower()) 

190 else: 

191 v = imsettings_env(True, im.lower(), im.lower(), im.lower(), im, "@im=%s" % im.lower()) 

192 warn("using input method settings: %s" % str(v)) 

193 warn("unknown input method specified: %s" % input_method) 

194 warn(" if it is correct, you may want to file a bug to get it recognized") 

195 

196def imsettings_env(disabled, gtk_im_module, qt_im_module, clutter_im_module, imsettings_module, xmodifiers): 

197 #for more information, see imsettings: 

198 #https://code.google.com/p/imsettings/source/browse/trunk/README 

199 if disabled is True: 

200 os.environ["DISABLE_IMSETTINGS"] = "1" #this should override any XSETTINGS too 

201 elif disabled is False and ("DISABLE_IMSETTINGS" in os.environ): 

202 del os.environ["DISABLE_IMSETTINGS"] 

203 v = { 

204 "GTK_IM_MODULE" : gtk_im_module, #or "gtk-im-context-simple"? 

205 "QT_IM_MODULE" : qt_im_module, #or "simple"? 

206 "QT4_IM_MODULE" : qt_im_module, 

207 "CLUTTER_IM_MODULE" : clutter_im_module, 

208 "IMSETTINGS_MODULE" : imsettings_module, #or "xim"? 

209 "XMODIFIERS" : xmodifiers, 

210 #not really sure what to do with those: 

211 #"IMSETTINGS_DISABLE_DESKTOP_CHECK" : "true", # 

212 #"IMSETTINGS_INTEGRATE_DESKTOP" : "no"} #we're not a real desktop 

213 } 

214 os.environ.update(v) 

215 return v 

216 

217def create_runtime_dir(xrd, uid, gid): 

218 if not POSIX or OSX or getuid()!=0 or (uid==0 and gid==0): 

219 return 

220 #workarounds: 

221 #* some distros don't set a correct value, 

222 #* or they don't create the directory for us, 

223 #* or pam_open is going to create the directory but needs time to do so.. 

224 if xrd and xrd.endswith("/user/0"): 

225 #don't keep root's directory, as this would not work: 

226 xrd = None 

227 if not xrd: 

228 #find the "/run/user" directory: 

229 run_user = "/run/user" 

230 if not os.path.exists(run_user): 

231 run_user = "/var/run/user" 

232 if os.path.exists(run_user): 

233 xrd = os.path.join(run_user, str(uid)) 

234 if not xrd: 

235 return None 

236 if not os.path.exists(xrd): 

237 os.mkdir(xrd, 0o700) 

238 os.lchown(xrd, uid, gid) 

239 xpra_dir = os.path.join(xrd, "xpra") 

240 if not os.path.exists(xpra_dir): 

241 os.mkdir(xpra_dir, 0o700) 

242 os.lchown(xpra_dir, uid, gid) 

243 return xrd 

244 

245 

246def guess_xpra_display(socket_dir, socket_dirs): 

247 dotxpra = DotXpra(socket_dir, socket_dirs) 

248 results = dotxpra.sockets() 

249 live = [display for state, display in results if state==DotXpra.LIVE] 

250 if not live: 

251 raise InitException("no existing xpra servers found") 

252 if len(live)>1: 

253 raise InitException("too many existing xpra servers found, cannot guess which one to use") 

254 return live[0] 

255 

256 

257def show_encoding_help(opts): 

258 #avoid errors and warnings: 

259 opts.encoding = "" 

260 opts.clipboard = False 

261 opts.notifications = False 

262 print("xpra server supports the following encodings:") 

263 print("(please wait, encoder initialization may take a few seconds)") 

264 #disable info logging which would be confusing here 

265 from xpra.log import get_all_loggers, set_default_level 

266 import logging 

267 set_default_level(logging.WARN) 

268 logging.root.setLevel(logging.WARN) 

269 for x in get_all_loggers(): 

270 if x.logger.getEffectiveLevel()==logging.INFO: 

271 x.logger.setLevel(logging.WARN) 

272 from xpra.server.server_base import ServerBase 

273 sb = ServerBase() 

274 sb.init(opts) 

275 from xpra.codecs.codec_constants import PREFERRED_ENCODING_ORDER, HELP_ORDER 

276 if "help" in opts.encodings: 

277 sb.allowed_encodings = PREFERRED_ENCODING_ORDER 

278 from xpra.server.mixins.encoding_server import EncodingServer 

279 assert isinstance(sb, EncodingServer) 

280 EncodingServer.threaded_setup(sb) 

281 from xpra.codecs.loader import encoding_help 

282 for e in (x for x in HELP_ORDER if x in sb.encodings): 

283 print(" * %s" % encoding_help(e)) 

284 return 0 

285 

286 

287def set_server_features(opts): 

288 def b(v): 

289 return str(v).lower() not in FALSE_OPTIONS 

290 #turn off some server mixins: 

291 from xpra.server import server_features 

292 impwarned = [] 

293 def impcheck(*modules): 

294 for mod in modules: 

295 try: 

296 __import__("xpra.%s" % mod, {}, {}, []) 

297 except ImportError: 

298 if mod not in impwarned: 

299 impwarned.append(mod) 

300 log = get_util_logger() 

301 log.warn("Warning: missing %s module", mod) 

302 return False 

303 return True 

304 server_features.notifications = opts.notifications and impcheck("notifications") 

305 server_features.webcam = b(opts.webcam) and impcheck("codecs") 

306 server_features.clipboard = b(opts.clipboard) and impcheck("clipboard") 

307 server_features.audio = (b(opts.speaker) or b(opts.microphone)) and impcheck("sound") 

308 server_features.av_sync = server_features.audio and b(opts.av_sync) 

309 server_features.fileprint = b(opts.printing) or b(opts.file_transfer) 

310 server_features.mmap = b(opts.mmap) 

311 server_features.input_devices = not opts.readonly and impcheck("keyboard") 

312 server_features.commands = impcheck("server.control_command") 

313 server_features.dbus = opts.dbus_proxy and impcheck("dbus", "server.dbus") 

314 server_features.encoding = impcheck("codecs") 

315 server_features.logging = b(opts.remote_logging) 

316 #server_features.network_state = ?? 

317 server_features.shell = envbool("XPRA_SHELL", True) 

318 server_features.display = opts.windows 

319 server_features.windows = opts.windows and impcheck("codecs") 

320 server_features.rfb = b(opts.rfb_upgrade) and impcheck("server.rfb") 

321 

322 

323def make_desktop_server(clobber): 

324 from xpra.x11.desktop_server import XpraDesktopServer 

325 return XpraDesktopServer(clobber) 

326 

327def make_server(clobber): 

328 from xpra.x11.server import XpraServer 

329 return XpraServer(clobber) 

330 

331def make_shadow_server(): 

332 from xpra.platform.shadow_server import ShadowServer 

333 return ShadowServer() 

334 

335def make_proxy_server(): 

336 from xpra.platform.proxy_server import ProxyServer 

337 return ProxyServer() 

338 

339 

340def verify_display(xvfb=None, display_name=None, shadowing=False, log_errors=True, timeout=None): 

341 #check that we can access the X11 display: 

342 from xpra.x11.vfb_util import verify_display_ready, VFB_WAIT 

343 if timeout is None: 

344 timeout = VFB_WAIT 

345 if not verify_display_ready(xvfb, display_name, shadowing, log_errors, timeout): 

346 return 1 

347 from xpra.log import Logger 

348 log = Logger("screen", "x11") 

349 log("X11 display is ready") 

350 no_gtk() 

351 from xpra.x11.gtk_x11.gdk_display_source import verify_gdk_display 

352 display = verify_gdk_display(display_name) 

353 if not display: 

354 return 1 

355 log("GDK can access the display") 

356 return 0 

357 

358def do_run_server(error_cb, opts, mode, xpra_file, extra_args, desktop_display=None, progress_cb=None): 

359 assert mode in ( 

360 "start", "start-desktop", 

361 "upgrade", "upgrade-desktop", 

362 "shadow", "proxy", 

363 ) 

364 

365 def _progress(i, msg): 

366 if progress_cb: 

367 progress_cb(i, msg) 

368 progress = _progress 

369 

370 progress(10, "initializing environment") 

371 try: 

372 cwd = os.getcwd() 

373 except OSError: 

374 cwd = os.path.expanduser("~") 

375 warn("current working directory does not exist, using '%s'\n" % cwd) 

376 validate_encryption(opts) 

377 if opts.encoding=="help" or "help" in opts.encodings: 

378 return show_encoding_help(opts) 

379 

380 #remove anything pointing to dbus from the current env 

381 #(so we only detect a dbus instance started by pam, 

382 # and override everything else) 

383 for k in tuple(os.environ.keys()): 

384 if k.startswith("DBUS_"): 

385 del os.environ[k] 

386 

387 use_display = parse_bool("use-display", opts.use_display) 

388 starting = mode == "start" 

389 starting_desktop = mode == "start-desktop" 

390 upgrading = mode == "upgrade" 

391 upgrading_desktop = mode == "upgrade-desktop" 

392 shadowing = mode == "shadow" 

393 proxying = mode == "proxy" 

394 

395 if not proxying and POSIX and not OSX: 

396 #we don't support wayland servers, 

397 #so make sure GDK will use the X11 backend: 

398 from xpra.os_util import saved_env 

399 saved_env["GDK_BACKEND"] = "x11" 

400 os.environ["GDK_BACKEND"] = "x11" 

401 

402 has_child_arg = ( 

403 opts.start_child or 

404 opts.start_child_on_connect or 

405 opts.start_child_after_connect or 

406 opts.start_child_on_last_client_exit 

407 ) 

408 if proxying or upgrading or upgrading_desktop: 

409 #when proxying or upgrading, don't exec any plain start commands: 

410 opts.start = opts.start_child = [] 

411 elif opts.exit_with_children: 

412 assert has_child_arg, "exit-with-children was specified but start-child* is missing!" 

413 elif opts.start_child: 

414 warn("Warning: the 'start-child' option is used,") 

415 warn(" but 'exit-with-children' is not enabled,") 

416 warn(" use 'start' instead") 

417 

418 if opts.bind_rfb and (proxying or starting): 

419 get_util_logger().warn("Warning: bind-rfb sockets cannot be used with '%s' mode" % mode) 

420 opts.bind_rfb = [] 

421 

422 if not shadowing and not starting_desktop: 

423 opts.rfb_upgrade = 0 

424 

425 if upgrading or upgrading_desktop or shadowing: 

426 #there should already be one running 

427 #so change None ('auto') to False 

428 if opts.pulseaudio is None: 

429 opts.pulseaudio = False 

430 

431 #get the display name: 

432 if shadowing and not extra_args: 

433 if WIN32 or OSX: 

434 #just a virtual name for the only display available: 

435 display_name = "Main" 

436 else: 

437 from xpra.scripts.main import guess_X11_display 

438 dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs) 

439 display_name = guess_X11_display(dotxpra, desktop_display) 

440 elif (upgrading or upgrading_desktop) and not extra_args: 

441 display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs) 

442 else: 

443 if len(extra_args) > 1: 

444 error_cb("too many extra arguments (%i): only expected a display number" % len(extra_args)) 

445 if len(extra_args) == 1: 

446 display_name = extra_args[0] 

447 if not shadowing and not upgrading and not use_display: 

448 display_name_check(display_name) 

449 else: 

450 if proxying: 

451 #find a free display number: 

452 dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs) 

453 all_displays = dotxpra.sockets() 

454 #ie: [("LIVE", ":100"), ("LIVE", ":200"), ...] 

455 displays = [v[1] for v in all_displays] 

456 display_name = None 

457 for x in range(1000, 20000): 

458 v = ":%s" % x 

459 if v not in displays: 

460 display_name = v 

461 break 

462 if not display_name: 

463 error_cb("you must specify a free virtual display name to use with the proxy server") 

464 elif use_display: 

465 #only use automatic guess for xpra displays and not X11 displays: 

466 display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs) 

467 else: 

468 # We will try to find one automaticaly 

469 # Use the temporary magic value 'S' as marker: 

470 display_name = 'S' + str(os.getpid()) 

471 

472 if not (shadowing or proxying or upgrading or upgrading_desktop) and \ 

473 opts.exit_with_children and not has_child_arg: 

474 error_cb("--exit-with-children specified without any children to spawn; exiting immediately") 

475 

476 atexit.register(run_cleanups) 

477 

478 # Generate the script text now, because os.getcwd() will 

479 # change if/when we daemonize: 

480 from xpra.server.server_util import ( 

481 xpra_runner_shell_script, 

482 write_runner_shell_scripts, 

483 find_log_dir, 

484 create_input_devices, 

485 source_env, 

486 ) 

487 script = None 

488 if POSIX and getuid()!=0: 

489 script = xpra_runner_shell_script(xpra_file, cwd, opts.socket_dir) 

490 

491 uid = int(opts.uid) 

492 gid = int(opts.gid) 

493 username = get_username_for_uid(uid) 

494 home = get_home_for_uid(uid) 

495 ROOT = POSIX and getuid()==0 

496 

497 protected_fds = [] 

498 protected_env = {} 

499 stdout = sys.stdout 

500 stderr = sys.stderr 

501 # Daemonize: 

502 if POSIX and opts.daemon: 

503 #daemonize will chdir to "/", so try to use an absolute path: 

504 if opts.password_file: 

505 opts.password_file = tuple(os.path.abspath(x) for x in opts.password_file) 

506 from xpra.server.server_util import daemonize 

507 daemonize() 

508 

509 displayfd = 0 

510 if POSIX and opts.displayfd: 

511 try: 

512 displayfd = int(opts.displayfd) 

513 if displayfd>0: 

514 protected_fds.append(displayfd) 

515 except ValueError as e: 

516 stderr.write("Error: invalid displayfd '%s':\n" % opts.displayfd) 

517 stderr.write(" %s\n" % e) 

518 del e 

519 

520 clobber = int(upgrading or upgrading_desktop)*CLOBBER_UPGRADE | int(use_display or 0)*CLOBBER_USE_DISPLAY 

521 start_vfb = not (shadowing or proxying or clobber) 

522 xauth_data = None 

523 if start_vfb: 

524 xauth_data = get_hex_uuid() 

525 

526 # if pam is present, try to create a new session: 

527 pam = None 

528 PAM_OPEN = POSIX and envbool("XPRA_PAM_OPEN", ROOT and uid!=0) 

529 if PAM_OPEN: 

530 try: 

531 from xpra.server.pam import pam_session #@UnresolvedImport 

532 except ImportError as e: 

533 stderr.write("Error: failed to import pam module\n") 

534 stderr.write(" %s" % e) 

535 del e 

536 PAM_OPEN = False 

537 if PAM_OPEN: 

538 fdc = FDChangeCaptureContext() 

539 with fdc: 

540 pam = pam_session(username) 

541 env = { 

542 #"XDG_SEAT" : "seat1", 

543 #"XDG_VTNR" : "0", 

544 "XDG_SESSION_TYPE" : "x11", 

545 #"XDG_SESSION_CLASS" : "user", 

546 "XDG_SESSION_DESKTOP" : "xpra", 

547 } 

548 #maybe we should just bail out instead? 

549 if pam.start(): 

550 pam.set_env(env) 

551 items = {} 

552 if display_name.startswith(":"): 

553 items["XDISPLAY"] = display_name 

554 if xauth_data: 

555 items["XAUTHDATA"] = xauth_data 

556 pam.set_items(items) 

557 if pam.open(): 

558 #we can't close it, because we're not going to be root any more, 

559 #but since we're the process leader for the session, 

560 #terminating will also close the session 

561 #add_cleanup(pam.close) 

562 protected_env = pam.get_envlist() 

563 os.environ.update(protected_env) 

564 #closing the pam fd causes the session to be closed, 

565 #and we don't want that! 

566 protected_fds += fdc.get_new_fds() 

567 

568 #get XDG_RUNTIME_DIR from env options, 

569 #which may not be have updated os.environ yet when running as root with "--uid=" 

570 xrd = os.path.abspath(parse_env(opts.env).get("XDG_RUNTIME_DIR", "")) 

571 if ROOT and (uid>0 or gid>0): 

572 #we're going to chown the directory if we create it, 

573 #ensure this cannot be abused, only use "safe" paths: 

574 if not any(x for x in ("/run/user/%i" % uid, "/tmp", "/var/tmp") if xrd.startswith(x)): 

575 xrd = "" 

576 #these paths could cause problems if we were to create and chown them: 

577 if xrd.startswith("/tmp/.X11-unix") or xrd.startswith("/tmp/.XIM-unix"): 

578 xrd = "" 

579 if not xrd: 

580 xrd = os.environ.get("XDG_RUNTIME_DIR") 

581 xrd = create_runtime_dir(xrd, uid, gid) 

582 if xrd: 

583 #this may override the value we get from pam 

584 #with the value supplied by the user: 

585 protected_env["XDG_RUNTIME_DIR"] = xrd 

586 

587 if script: 

588 # Write out a shell-script so that we can start our proxy in a clean 

589 # environment: 

590 write_runner_shell_scripts(script) 

591 

592 import datetime 

593 extra_expand = {"TIMESTAMP" : datetime.datetime.now().strftime("%Y%m%d-%H%M%S")} 

594 log_to_file = opts.daemon or os.environ.get("XPRA_LOG_TO_FILE", "")=="1" 

595 if start_vfb or log_to_file: 

596 #we will probably need a log dir 

597 #either for the vfb, or for our own log file 

598 log_dir = opts.log_dir or "" 

599 if not log_dir or log_dir.lower()=="auto": 

600 log_dir = find_log_dir(username, uid=uid, gid=gid) 

601 if not log_dir: 

602 raise InitException("cannot find or create a logging directory") 

603 #expose the log-dir as "XPRA_LOG_DIR", 

604 #this is used by Xdummy for the Xorg log file 

605 if "XPRA_LOG_DIR" not in os.environ: 

606 os.environ["XPRA_LOG_DIR"] = log_dir 

607 

608 if log_to_file: 

609 from xpra.server.server_util import select_log_file, open_log_file, redirect_std_to_log 

610 log_filename0 = osexpand(select_log_file(log_dir, opts.log_file, display_name), 

611 username, uid, gid, extra_expand) 

612 if os.path.exists(log_filename0) and not display_name.startswith("S"): 

613 #don't overwrite the log file just yet, 

614 #as we may still fail to start 

615 log_filename0 += ".new" 

616 logfd = open_log_file(log_filename0) 

617 if POSIX and ROOT and (uid>0 or gid>0): 

618 try: 

619 os.fchown(logfd, uid, gid) 

620 except OSError as e: 

621 noerr(stderr.write, "failed to chown the log file '%s'\n" % log_filename0) 

622 noerr(stderr.flush) 

623 stdout, stderr = redirect_std_to_log(logfd, *protected_fds) 

624 noerr(stderr.write, "Entering daemon mode; " 

625 + "any further errors will be reported to:\n" 

626 + (" %s\n" % log_filename0)) 

627 noerr(stderr.flush) 

628 os.environ["XPRA_SERVER_LOG"] = log_filename0 

629 else: 

630 #server log does not exist: 

631 os.environ.pop("XPRA_SERVER_LOG", None) 

632 

633 #warn early about this: 

634 if (starting or starting_desktop) and desktop_display and opts.notifications and not opts.dbus_launch: 

635 print_DE_warnings() 

636 

637 if start_vfb and opts.xvfb.find("Xephyr")>=0 and opts.sync_xvfb<=0: 

638 warn("Warning: using Xephyr as vfb") 

639 warn(" you should also enable the sync-xvfb option") 

640 warn(" to keep the Xephyr window updated") 

641 

642 progress(10, "creating sockets") 

643 from xpra.net.socket_util import get_network_logger, setup_local_sockets, create_sockets 

644 sockets = create_sockets(opts, error_cb) 

645 

646 sanitize_env() 

647 os.environ.update(source_env(opts.source)) 

648 if POSIX: 

649 if xrd: 

650 os.environ["XDG_RUNTIME_DIR"] = xrd 

651 if not OSX: 

652 os.environ["XDG_SESSION_TYPE"] = "x11" 

653 if not starting_desktop: 

654 os.environ["XDG_CURRENT_DESKTOP"] = opts.wm_name 

655 configure_imsettings_env(opts.input_method) 

656 if display_name[0] != 'S': 

657 os.environ["DISPLAY"] = display_name 

658 if POSIX: 

659 os.environ["CKCON_X11_DISPLAY"] = display_name 

660 elif not start_vfb or opts.xvfb.find("Xephyr")<0: 

661 os.environ.pop("DISPLAY", None) 

662 os.environ.update(protected_env) 

663 from xpra.log import Logger 

664 log = Logger("server") 

665 log("env=%s", os.environ) 

666 

667 UINPUT_UUID_LEN = 12 

668 UINPUT_UUID_MIN_LEN = 12 

669 UINPUT_UUID_MAX_LEN = 32 

670 # Start the Xvfb server first to get the display_name if needed 

671 odisplay_name = display_name 

672 xvfb = None 

673 xvfb_pid = None 

674 uinput_uuid = None 

675 if start_vfb and use_display is None: 

676 #use-display='auto' so we have to figure out 

677 #if we have to start the vfb or not: 

678 if not display_name: 

679 use_display = False 

680 else: 

681 progress(20, "connecting to the display") 

682 start_vfb = verify_display(None, display_name, log_errors=False, timeout=1)!=0 

683 if start_vfb: 

684 progress(20, "starting a virtual display") 

685 assert not proxying and xauth_data 

686 pixel_depth = validate_pixel_depth(opts.pixel_depth, starting_desktop) 

687 from xpra.x11.vfb_util import start_Xvfb, check_xvfb_process, parse_resolution 

688 from xpra.server.server_util import has_uinput 

689 uinput_uuid = None 

690 if has_uinput() and opts.input_devices.lower() in ("uinput", "auto") and not shadowing: 

691 from xpra.os_util import get_rand_chars 

692 uinput_uuid = get_rand_chars(UINPUT_UUID_LEN) 

693 vfb_geom = "" 

694 try: 

695 vfb_geom = parse_resolution(opts.resize_display) 

696 except Exception: 

697 pass 

698 xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, vfb_geom, pixel_depth, display_name, cwd, 

699 uid, gid, username, xauth_data, uinput_uuid) 

700 for f in cleanups: 

701 add_cleanup(f) 

702 xvfb_pid = xvfb.pid 

703 #always update as we may now have the "real" display name: 

704 os.environ["DISPLAY"] = display_name 

705 os.environ["CKCON_X11_DISPLAY"] = display_name 

706 os.environ.update(protected_env) 

707 if display_name!=odisplay_name and pam: 

708 pam.set_items({"XDISPLAY" : display_name}) 

709 

710 def check_xvfb(timeout=0): 

711 return check_xvfb_process(xvfb, timeout=timeout, command=opts.xvfb) 

712 else: 

713 if POSIX and clobber: 

714 #if we're meant to be using a private XAUTHORITY file, 

715 #make sure to point to it: 

716 from xpra.x11.vfb_util import get_xauthority_path 

717 xauthority = get_xauthority_path(display_name, username, uid, gid) 

718 if os.path.exists(xauthority): 

719 log("found XAUTHORITY=%s", xauthority) 

720 os.environ["XAUTHORITY"] = xauthority 

721 def check_xvfb(timeout=0): 

722 return True 

723 

724 if POSIX and not OSX and displayfd>0: 

725 from xpra.platform.displayfd import write_displayfd 

726 try: 

727 display_no = display_name[1:] 

728 #ensure it is a string containing the number: 

729 display_no = str(int(display_no)) 

730 log("writing display_no='%s' to displayfd=%i", display_no, displayfd) 

731 assert write_displayfd(displayfd, display_no), "timeout" 

732 except Exception as e: 

733 log.error("write_displayfd failed", exc_info=True) 

734 log.error("Error: failed to write '%s' to fd=%s", display_name, displayfd) 

735 log.error(" %s", str(e) or type(e)) 

736 del e 

737 

738 if not check_xvfb(1): 

739 noerr(stderr.write, "vfb failed to start, exiting\n") 

740 return EXIT_VFB_ERROR 

741 

742 if WIN32 and os.environ.get("XPRA_LOG_FILENAME"): 

743 os.environ["XPRA_SERVER_LOG"] = os.environ["XPRA_LOG_FILENAME"] 

744 if opts.daemon: 

745 log_filename1 = osexpand(select_log_file(log_dir, opts.log_file, display_name), 

746 username, uid, gid, extra_expand) 

747 if log_filename0 != log_filename1: 

748 # we now have the correct log filename, so use it: 

749 try: 

750 os.rename(log_filename0, log_filename1) 

751 except (OSError, IOError): 

752 pass 

753 else: 

754 os.environ["XPRA_SERVER_LOG"] = log_filename1 

755 if odisplay_name!=display_name: 

756 #this may be used by scripts, let's try not to change it: 

757 noerr(stderr.write, "Actual display used: %s\n" % display_name) 

758 noerr(stderr.write, "Actual log file name is now: %s\n" % log_filename1) 

759 noerr(stderr.flush) 

760 noerr(stdout.close) 

761 noerr(stderr.close) 

762 #we should not be using stdout or stderr from this point: 

763 del stdout 

764 del stderr 

765 

766 if not check_xvfb(): 

767 noerr(stderr.write, "vfb failed to start, exiting\n") 

768 return EXIT_VFB_ERROR 

769 

770 #create devices for vfb if needed: 

771 devices = {} 

772 if not start_vfb and not proxying and not shadowing and envbool("XPRA_UINPUT", True): 

773 #try to find the existing uinput uuid: 

774 #use a subprocess to avoid polluting our current process 

775 #with X11 connections before we get a chance to change uid 

776 prop = "_XPRA_UINPUT_ID" 

777 cmd = ["xprop", "-display", display_name, "-root", prop] 

778 log("looking for '%s' on display '%s' with XAUTHORITY='%s'", prop, display_name, os.environ.get("XAUTHORITY")) 

779 try: 

780 code, out, err = get_status_output(cmd) 

781 except Exception as e: 

782 log("failed to get existing uinput id: %s", e) 

783 del e 

784 else: 

785 log("Popen(%s)=%s", cmd, (code, out, err)) 

786 if code==0 and out.find("=")>0: 

787 uinput_uuid = out.split("=", 1)[1] 

788 log("raw uinput uuid=%s", uinput_uuid) 

789 uinput_uuid = strtobytes(uinput_uuid.strip('\n\r"\\ ')) 

790 if uinput_uuid: 

791 if len(uinput_uuid)>UINPUT_UUID_MAX_LEN or len(uinput_uuid)<UINPUT_UUID_MIN_LEN: 

792 log.warn("Warning: ignoring invalid uinput id:") 

793 log.warn(" '%s'", uinput_uuid) 

794 uinput_uuid = None 

795 else: 

796 log.info("retrieved existing uinput id: %s", bytestostr(uinput_uuid)) 

797 if uinput_uuid: 

798 devices = create_input_devices(uinput_uuid, uid) 

799 

800 if ROOT and (uid!=0 or gid!=0): 

801 log("root: switching to uid=%i, gid=%i", uid, gid) 

802 setuidgid(uid, gid) 

803 os.environ.update({ 

804 "HOME" : home, 

805 "USER" : username, 

806 "LOGNAME" : username, 

807 }) 

808 shell = get_shell_for_uid(uid) 

809 if shell: 

810 os.environ["SHELL"] = shell 

811 #now we've changed uid, it is safe to honour all the env updates: 

812 configure_env(opts.env) 

813 os.environ.update(protected_env) 

814 

815 if opts.chdir: 

816 log("chdir(%s)", opts.chdir) 

817 os.chdir(opts.chdir) 

818 

819 dbus_pid, dbus_env = 0, {} 

820 if not shadowing and POSIX and not OSX and not clobber: 

821 no_gtk() 

822 assert starting or starting_desktop or proxying 

823 try: 

824 from xpra.server.dbus.dbus_start import start_dbus 

825 except ImportError as e: 

826 log("dbus components are not installed: %s", e) 

827 else: 

828 dbus_pid, dbus_env = start_dbus(opts.dbus_launch) 

829 if dbus_env: 

830 os.environ.update(dbus_env) 

831 

832 if not proxying: 

833 if POSIX and not OSX: 

834 no_gtk() 

835 if starting or starting_desktop or shadowing: 

836 r = verify_display(xvfb, display_name, shadowing) 

837 if r: 

838 return r 

839 #on win32, this ensures that we get the correct screen size to shadow: 

840 from xpra.platform.gui import init as gui_init 

841 log("gui_init()") 

842 gui_init() 

843 

844 progress(50, "creating local sockets") 

845 #setup unix domain socket: 

846 netlog = get_network_logger() 

847 local_sockets = setup_local_sockets(opts.bind, 

848 opts.socket_dir, opts.socket_dirs, 

849 display_name, clobber, 

850 opts.mmap_group, opts.socket_permissions, 

851 username, uid, gid) 

852 netlog("setting up local sockets: %s", local_sockets) 

853 sockets.update(local_sockets) 

854 if POSIX and (starting or upgrading or starting_desktop or upgrading_desktop): 

855 #all unix domain sockets: 

856 ud_paths = [sockpath for stype, _, sockpath, _ in local_sockets if stype=="unix-domain"] 

857 if ud_paths: 

858 #choose one so our xdg-open override script can use to talk back to us: 

859 if opts.forward_xdg_open: 

860 for x in ("/usr/libexec/xpra", "/usr/lib/xpra"): 

861 xdg_override = os.path.join(x, "xdg-open") 

862 if os.path.exists(xdg_override): 

863 os.environ["PATH"] = x+os.pathsep+os.environ.get("PATH", "") 

864 os.environ["XPRA_SERVER_SOCKET"] = ud_paths[0] 

865 break 

866 else: 

867 log.warn("Warning: no local server sockets,") 

868 if opts.forward_xdg_open: 

869 log.warn(" forward-xdg-open cannot be enabled") 

870 log.warn(" non-embedded ssh connections will not be available") 

871 

872 set_server_features(opts) 

873 

874 if not proxying and POSIX and not OSX: 

875 if not check_xvfb(): 

876 return 1 

877 from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source 

878 if os.environ.get("NO_AT_BRIDGE") is None: 

879 os.environ["NO_AT_BRIDGE"] = "1" 

880 init_gdk_display_source() 

881 #(now we can access the X11 server) 

882 if uinput_uuid: 

883 save_uinput_id(uinput_uuid) 

884 

885 progress(60, "initializing server") 

886 if shadowing: 

887 app = make_shadow_server() 

888 elif proxying: 

889 app = make_proxy_server() 

890 else: 

891 if starting or upgrading: 

892 app = make_server(clobber) 

893 else: 

894 assert starting_desktop or upgrading_desktop 

895 app = make_desktop_server(clobber) 

896 app.init_virtual_devices(devices) 

897 

898 try: 

899 app.exec_cwd = opts.chdir or cwd 

900 app.display_name = display_name 

901 app.init(opts) 

902 progress(70, "initializing sockets") 

903 app.init_sockets(sockets) 

904 app.init_dbus(dbus_pid, dbus_env) 

905 if not shadowing and not proxying: 

906 app.init_display_pid(xvfb_pid) 

907 app.original_desktop_display = desktop_display 

908 del opts 

909 if not app.server_ready(): 

910 return 1 

911 progress(80, "finalizing") 

912 app.server_init() 

913 app.setup() 

914 app.init_when_ready(_when_ready) 

915 except InitException as e: 

916 log.error("xpra server initialization error:") 

917 log.error(" %s", e) 

918 app.cleanup() 

919 return 1 

920 except Exception as e: 

921 log.error("Error: cannot start the %s server", app.session_type, exc_info=True) 

922 log.error(str(e)) 

923 log.info("") 

924 if upgrading or upgrading_desktop: 

925 #something abnormal occurred, 

926 #don't kill the vfb on exit: 

927 from xpra.server import EXITING_CODE 

928 app._upgrading = EXITING_CODE 

929 app.cleanup() 

930 return 1 

931 

932 try: 

933 progress(100, "running") 

934 log("running %s", app.run) 

935 r = app.run() 

936 log("%s()=%s", app.run, r) 

937 except KeyboardInterrupt: 

938 log.info("stopping on KeyboardInterrupt") 

939 app.cleanup() 

940 return EXIT_OK 

941 except Exception: 

942 log.error("server error", exc_info=True) 

943 app.cleanup() 

944 return -128 

945 else: 

946 if r>0: 

947 r = 0 

948 return r