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#!/usr/bin/env python 

2# This file is part of Xpra. 

3# Copyright (C) 2011 Serviware (Arthur Huillet, <ahuillet@serviware.com>) 

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

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

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

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

8 

9import sys 

10import os.path 

11import stat 

12import socket 

13from time import sleep 

14import logging 

15from subprocess import Popen, PIPE, TimeoutExpired 

16import signal 

17import shlex 

18import traceback 

19 

20from xpra.platform.dotxpra import DotXpra 

21from xpra.util import ( 

22 csv, envbool, envint, nonl, pver, parse_simple_dict, noerr, 

23 DEFAULT_PORT, DEFAULT_PORTS, 

24 ) 

25from xpra.exit_codes import ( 

26 EXIT_STR, 

27 EXIT_OK, EXIT_FAILURE, EXIT_UNSUPPORTED, EXIT_CONNECTION_FAILED, 

28 EXIT_NO_DISPLAY, 

29 EXIT_CONNECTION_LOST, EXIT_REMOTE_ERROR, 

30 EXIT_INTERNAL_ERROR, EXIT_FILE_TOO_BIG, 

31 ) 

32from xpra.os_util import ( 

33 get_util_logger, getuid, getgid, 

34 monotonic_time, bytestostr, use_tty, 

35 WIN32, OSX, POSIX, SIGNAMES, is_Ubuntu, 

36 ) 

37from xpra.scripts.parsing import ( 

38 info, warn, error, 

39 parse_vsock, parse_env, is_local, 

40 fixup_defaults, validated_encodings, validate_encryption, do_parse_cmdline, show_sound_codec_help, 

41 supports_shadow, supports_server, supports_proxy, supports_mdns, 

42 ) 

43from xpra.scripts.config import ( 

44 OPTION_TYPES, TRUE_OPTIONS, FALSE_OPTIONS, OFF_OPTIONS, 

45 CLIENT_OPTIONS, NON_COMMAND_LINE_OPTIONS, CLIENT_ONLY_OPTIONS, 

46 START_COMMAND_OPTIONS, BIND_OPTIONS, PROXY_START_OVERRIDABLE_OPTIONS, OPTIONS_ADDED_SINCE_V3, OPTIONS_COMPAT_NAMES, 

47 InitException, InitInfo, InitExit, 

48 fixup_options, dict_to_validated_config, get_xpra_defaults_dirs, get_defaults, read_xpra_conf, 

49 make_defaults_struct, parse_bool, has_sound_support, name_to_field, 

50 ) 

51from xpra.log import is_debug_enabled, Logger, get_debug_args 

52assert info and warn and error, "used by modules importing those from here" 

53 

54NO_ROOT_WARNING = envbool("XPRA_NO_ROOT_WARNING", False) 

55CLIPBOARD_CLASS = os.environ.get("XPRA_CLIPBOARD_CLASS") 

56WAIT_SERVER_TIMEOUT = envint("WAIT_SERVER_TIMEOUT", 90) 

57CONNECT_TIMEOUT = envint("XPRA_CONNECT_TIMEOUT", 20) 

58OPENGL_PROBE_TIMEOUT = envint("XPRA_OPENGL_PROBE_TIMEOUT", 5) 

59SYSTEMD_RUN = envbool("XPRA_SYSTEMD_RUN", True) 

60VERIFY_X11_SOCKET_TIMEOUT = envint("XPRA_VERIFY_X11_SOCKET_TIMEOUT", 1) 

61LIST_REPROBE_TIMEOUT = envint("XPRA_LIST_REPROBE_TIMEOUT", 10) 

62 

63 

64def nox(): 

65 DISPLAY = os.environ.get("DISPLAY") 

66 if DISPLAY is not None: 

67 del os.environ["DISPLAY"] 

68 # This is an error on Fedora/RH, so make it an error everywhere so it will 

69 # be noticed: 

70 import warnings 

71 warnings.filterwarnings("error", "could not open display") 

72 return DISPLAY 

73 

74def werr(*msg): 

75 for x in msg: 

76 noerr(sys.stderr.write, "%s\n" % (x,)) 

77 noerr(sys.stderr.flush) 

78 

79 

80def main(script_file, cmdline): 

81 ml = envint("XPRA_MEM_USAGE_LOGGER") 

82 if ml>0: 

83 from xpra.util import start_mem_watcher 

84 start_mem_watcher(ml) 

85 

86 if sys.flags.optimize>0: # pragma: no cover 

87 sys.stderr.write("************************************************************\n") 

88 sys.stderr.write("Warning: the python optimize flag is set to %i\n" % sys.flags.optimize) 

89 sys.stderr.write(" xpra is very likely to crash\n") 

90 sys.stderr.write("************************************************************\n") 

91 sleep(5) 

92 

93 from xpra.platform import clean as platform_clean, command_error, command_info 

94 if len(cmdline)==1: 

95 cmdline.append("gui") 

96 

97 #turn off gdk scaling to make sure we get the actual window geometry: 

98 os.environ["GDK_SCALE"]="1" 

99 os.environ["GDK_DPI_SCALE"] = "1" 

100 #client side decorations break window geometry, 

101 #disable this "feature" unless explicitly enabled: 

102 if os.environ.get("GTK_CSD") is None: 

103 os.environ["GTK_CSD"] = "0" 

104 if POSIX and not OSX and os.environ.get("XDG_SESSION_TYPE", "x11")=="x11" and not os.environ.get("GDK_BACKEND"): 

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

106 

107 if envbool("XPRA_NOMD5", False): 

108 import hashlib 

109 def nomd5(*_args): 

110 raise ValueError("md5 support is disabled") 

111 hashlib.algorithms_available.remove("md5") 

112 hashlib.md5 = nomd5 

113 

114 def debug_exc(msg="run_mode error"): 

115 get_util_logger().debug(msg, exc_info=True) 

116 

117 try: 

118 defaults = make_defaults_struct() 

119 fixup_defaults(defaults) 

120 options, args = do_parse_cmdline(cmdline, defaults) 

121 if not args: 

122 raise InitExit(-1, "xpra: need a mode") 

123 mode = args.pop(0) 

124 def err(*args): 

125 raise InitException(*args) 

126 return run_mode(script_file, err, options, args, mode, defaults) 

127 except SystemExit as e: 

128 debug_exc() 

129 return e.code 

130 except InitExit as e: 

131 debug_exc() 

132 if str(e) and e.args and (e.args[0] or len(e.args)>1): 

133 command_info("%s" % e) 

134 return e.status 

135 except InitInfo as e: 

136 debug_exc() 

137 command_info("%s" % e) 

138 return 0 

139 except InitException as e: 

140 debug_exc() 

141 command_error("xpra initialization error:\n %s" % e) 

142 return 1 

143 except AssertionError as e: 

144 debug_exc() 

145 command_error("xpra initialization error:\n %s" % e) 

146 traceback.print_tb(sys.exc_info()[2]) 

147 return 1 

148 except Exception: 

149 debug_exc() 

150 command_error("xpra main error:\n%s" % traceback.format_exc()) 

151 return 1 

152 finally: 

153 platform_clean() 

154 def closestd(std): 

155 if std: 

156 try: 

157 std.close() 

158 except OSError: # pragma: no cover 

159 pass 

160 closestd(sys.stdout) 

161 closestd(sys.stderr) 

162 

163 

164def configure_logging(options, mode): 

165 if mode in ( 

166 "showconfig", "info", "id", "attach", "listen", "launcher", "stop", "print", 

167 "control", "list", "list-windows", "list-mdns", "sessions", "mdns-gui", "bug-report", 

168 "splash", "qrcode", 

169 "opengl-test", 

170 "test-connect", 

171 "encoding", "webcam", "clipboard-test", 

172 "keyboard", "keyboard-test", "keymap", "gui-info", "network-info", "path-info", 

173 "printing-info", "version-info", "gtk-info", 

174 "colors-test", "colors-gradient-test", "transparent-colors", "transparent-window", 

175 "auth", 

176 ): 

177 s = sys.stdout 

178 else: 

179 s = sys.stderr 

180 to = s 

181 try: 

182 import codecs 

183 #print("locale.getpreferredencoding()=%s" % (locale.getpreferredencoding(),)) 

184 #python3 has a buffer attribute, 

185 #which we must use if we want to be able to write bytes: 

186 sbuf = getattr(s, "buffer", s) 

187 to = codecs.getwriter("utf-8")(sbuf, "replace") 

188 except Exception: # pragma: no cover 

189 pass 

190 #a bit naughty here, but it's easier to let xpra.log initialize 

191 #the logging system every time, and just undo things here.. 

192 from xpra.log import ( 

193 setloghandler, enable_color, enable_format, 

194 LOG_FORMAT, NOPREFIX_FORMAT, 

195 SIGPIPEStreamHandler, 

196 ) 

197 setloghandler(SIGPIPEStreamHandler(to)) 

198 if mode in ( 

199 "start", "start-desktop", "upgrade", "upgrade-desktop", 

200 "attach", "listen", "shadow", "proxy", 

201 "_sound_record", "_sound_play", 

202 "stop", "print", "showconfig", 

203 "request-start", "request-start-desktop", "request-shadow", 

204 "_dialog", "_pass", 

205 ): 

206 if "help" in options.speaker_codec or "help" in options.microphone_codec: 

207 server_mode = mode not in ("attach", "listen") 

208 codec_help = show_sound_codec_help(server_mode, options.speaker_codec, options.microphone_codec) 

209 raise InitInfo("\n".join(codec_help)) 

210 fmt = LOG_FORMAT 

211 if mode in ("stop", "showconfig"): 

212 fmt = NOPREFIX_FORMAT 

213 if envbool("XPRA_COLOR_LOG", hasattr(to, "fileno") and os.isatty(to.fileno())): 

214 enable_color(to, fmt) 

215 else: 

216 enable_format(fmt) 

217 

218 from xpra.log import add_debug_category, add_disabled_category, enable_debug_for, disable_debug_for 

219 if options.debug: 

220 categories = options.debug.split(",") 

221 for cat in categories: 

222 if not cat: 

223 continue 

224 if cat[0]=="-": 

225 add_disabled_category(cat[1:]) 

226 disable_debug_for(cat[1:]) 

227 else: 

228 add_debug_category(cat) 

229 enable_debug_for(cat) 

230 

231 #always log debug level, we just use it selectively (see above) 

232 logging.root.setLevel(logging.INFO) 

233 

234 

235def configure_network(options): 

236 from xpra.net import compression, packet_encoding 

237 ecs = compression.get_enabled_compressors() 

238 for c in compression.ALL_COMPRESSORS: 

239 enabled = c in ecs and c in options.compressors 

240 setattr(compression, "use_%s" % c, enabled) 

241 if not ecs: 

242 #force compression level to zero since we have no compressors available: 

243 options.compression_level = 0 

244 ees = packet_encoding.get_enabled_encoders() 

245 count = 0 

246 for pe in packet_encoding.ALL_ENCODERS: 

247 enabled = pe in ees and pe in options.packet_encoders 

248 setattr(packet_encoding, "use_%s" % pe, enabled) 

249 count += int(enabled) 

250 #verify that at least one encoder is available: 

251 if not count: 

252 raise InitException("at least one valid packet encoder must be enabled") 

253 

254def configure_env(env_str): 

255 if env_str: 

256 env = parse_env(env_str) 

257 if POSIX and getuid()==0: 

258 #running as root! 

259 #sanitize: only allow "safe" environment variables 

260 #as these may have been specified by a non-root user 

261 env = dict((k,v) for k,v in env.items() if k.startswith("XPRA_")) 

262 os.environ.update(env) 

263 

264 

265def systemd_run_command(mode, systemd_run_args=None, user=True): 

266 cmd = ["systemd-run", "--description" , "xpra-%s" % mode, "--scope"] 

267 if user: 

268 cmd.append("--user") 

269 LOG_SYSTEMD_WRAP = envbool("XPRA_LOG_SYSTEMD_WRAP", True) 

270 if not LOG_SYSTEMD_WRAP: 

271 cmd.append("--quiet") 

272 if systemd_run_args: 

273 cmd += shlex.split(systemd_run_args) 

274 return cmd 

275 

276def systemd_run_wrap(mode, args, systemd_run_args=None, **kwargs): 

277 cmd = systemd_run_command(mode, systemd_run_args) 

278 cmd += args 

279 cmd.append("--systemd-run=no") 

280 stderr = sys.stderr 

281 LOG_SYSTEMD_WRAP = envbool("XPRA_LOG_SYSTEMD_WRAP", True) 

282 if LOG_SYSTEMD_WRAP: 

283 noerr(stderr.write, "using systemd-run to wrap '%s' server command\n" % mode) 

284 LOG_SYSTEMD_WRAP_COMMAND = envbool("XPRA_LOG_SYSTEMD_WRAP_COMMAND", False) 

285 if LOG_SYSTEMD_WRAP_COMMAND: 

286 noerr(stderr.write, "%s\n" % " ".join(["'%s'" % x for x in cmd])) 

287 try: 

288 p = Popen(cmd, **kwargs) 

289 return p.wait() 

290 except KeyboardInterrupt: 

291 return 128+signal.SIGINT 

292 

293 

294def isdisplaytype(args, *dtypes) -> bool: 

295 if not args: 

296 return False 

297 d = args[0] 

298 return any((d.startswith("%s/" % dtype) or d.startswith("%s:" % dtype) for dtype in dtypes)) 

299 

300def check_gtk(): 

301 import gi 

302 gi.require_version("Gtk", "3.0") 

303 from gi.repository import Gtk 

304 assert Gtk 

305 r = Gtk.init_check(None) 

306 if not r[0]: 

307 raise InitExit(EXIT_NO_DISPLAY, "failed to initialize Gtk, no display?") 

308 check_display() 

309 

310def check_display(): 

311 from xpra.platform.gui import can_access_display 

312 if not can_access_display(): # pragma: no cover 

313 raise InitExit(EXIT_NO_DISPLAY, "cannot access display") 

314 

315def use_systemd_run(s): 

316 if not SYSTEMD_RUN or not POSIX: 

317 return False # pragma: no cover 

318 systemd_run = parse_bool("systemd-run", s) 

319 if systemd_run in (True, False): 

320 return systemd_run 

321 #detect if we should use it: 

322 if is_Ubuntu() and (os.environ.get("SSH_TTY") or os.environ.get("SSH_CLIENT")): # pragma: no cover 

323 #would fail 

324 return False 

325 from xpra.os_util import is_systemd_pid1 

326 if not is_systemd_pid1(): 

327 return False # pragma: no cover 

328 #test it: 

329 cmd = ["systemd-run", "--quiet", "--user", "--scope", "--", "true"] 

330 proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=False) 

331 try: 

332 proc.communicate(timeout=2) 

333 r = proc.returncode 

334 except TimeoutExpired: # pragma: no cover 

335 r = None 

336 if r is None: 

337 try: 

338 proc.terminate() 

339 except Exception: 

340 pass 

341 try: 

342 proc.communicate(timeout=1) 

343 except TimeoutExpired: # pragma: no cover 

344 r = None 

345 return r==0 

346 

347 

348def run_mode(script_file, error_cb, options, args, mode, defaults): 

349 #configure default logging handler: 

350 if POSIX and getuid()==0 and options.uid==0 and mode!="proxy" and not NO_ROOT_WARNING: 

351 warn("\nWarning: running as root") 

352 

353 display_is_remote = isdisplaytype(args, "ssh", "tcp", "ssl", "vsock") 

354 if mode in ("start", "start-desktop", "upgrade", "upgrade-desktop", "shadow") and not display_is_remote: 

355 if use_systemd_run(options.systemd_run): 

356 #make sure we run via the same interpreter, 

357 #inject it into the command line if we have to: 

358 argv = list(sys.argv) 

359 if argv[0].find("python")<0: 

360 argv.insert(0, "python%i.%i" % (sys.version_info.major, sys.version_info.minor)) 

361 return systemd_run_wrap(mode, argv, options.systemd_run_args) 

362 configure_env(options.env) 

363 configure_logging(options, mode) 

364 configure_network(options) 

365 

366 if mode not in ("showconfig", "splash") and POSIX and not OSX and os.environ.get("XDG_RUNTIME_DIR") is None and getuid()>0: 

367 xrd = "/run/user/%i" % getuid() 

368 if os.path.exists(xrd): 

369 warn("Warning: using '%s' as XDG_RUNTIME_DIR" % xrd) 

370 os.environ["XDG_RUNTIME_DIR"] = xrd 

371 else: 

372 warn("Warning: XDG_RUNTIME_DIR is not defined") 

373 warn(" and '%s' does not exist" % xrd) 

374 if os.path.exists("/tmp") and os.path.isdir("/tmp"): 

375 xrd = "/tmp" 

376 warn(" using '%s'" % xrd) 

377 os.environ["XDG_RUNTIME_DIR"] = xrd 

378 

379 if not mode.startswith("_sound_"): 

380 #only the sound subcommands should ever actually import GStreamer: 

381 try: 

382 from xpra.sound.gstreamer_util import prevent_import 

383 prevent_import() 

384 except ImportError: # pragma: no cover 

385 pass 

386 #sound commands don't want to set the name 

387 #(they do it later to prevent glib import conflicts) 

388 #"attach" does it when it received the session name from the server 

389 if mode not in ("attach", "listen", "start", "start-desktop", "upgrade", "upgrade-desktop", "proxy", "shadow"): 

390 from xpra.platform import set_name 

391 set_name("Xpra", "Xpra %s" % mode.strip("_")) 

392 

393 if mode in ( 

394 "start", "start-desktop", 

395 "shadow", "attach", "listen", 

396 "request-start", "request-start-desktop", "request-shadow", 

397 ): 

398 options.encodings = validated_encodings(options.encodings) 

399 try: 

400 return do_run_mode(script_file, error_cb, options, args, mode, defaults) 

401 except KeyboardInterrupt as e: 

402 info("\ncaught %s, exiting" % repr(e)) 

403 return 128+signal.SIGINT 

404 

405 

406def do_run_mode(script_file, error_cb, options, args, mode, defaults): 

407 display_is_remote = isdisplaytype(args, "ssh", "tcp", "ssl", "vsock") 

408 if mode in ("start", "start-desktop", "shadow") and display_is_remote: 

409 #ie: "xpra start ssh://USER@HOST:SSHPORT/DISPLAY --start-child=xterm" 

410 return run_remote_server(error_cb, options, args, mode, defaults) 

411 

412 if mode in ("start", "start-desktop") and args and parse_bool("attach", options.attach) is True: 

413 assert not display_is_remote 

414 #maybe the server is already running 

415 #and we don't need to bother trying to start it: 

416 try: 

417 display = pick_display(error_cb, options, args) 

418 except Exception: 

419 pass 

420 else: 

421 dotxpra = DotXpra(options.socket_dir, options.socket_dirs) 

422 display_name = display.get("display_name") 

423 if display_name: 

424 state = dotxpra.get_display_state(display_name) 

425 if state==DotXpra.LIVE: 

426 noerr(sys.stdout.write, "existing live display found, attaching") 

427 return do_run_mode(script_file, error_cb, options, args, "attach", defaults) 

428 

429 if (mode in ("start", "start-desktop", "upgrade", "upgrade-desktop") and supports_server) or \ 

430 (mode=="shadow" and supports_shadow) or (mode=="proxy" and supports_proxy): 

431 return run_server(script_file, error_cb, options, args, mode, defaults) 

432 elif mode in ( 

433 "attach", "listen", "detach", 

434 "screenshot", "version", "info", "id", 

435 "control", "_monitor", "shell", "print", 

436 "qrcode", 

437 "connect-test", "request-start", "request-start-desktop", "request-shadow", 

438 ): 

439 return run_client(error_cb, options, args, mode) 

440 elif mode in ("stop", "exit"): 

441 return run_stopexit(mode, error_cb, options, args) 

442 elif mode == "top": 

443 return run_top(error_cb, options, args) 

444 elif mode == "list": 

445 return run_list(error_cb, options, args) 

446 elif mode == "list-windows": 

447 return run_list_windows(error_cb, options, args) 

448 elif mode == "list-mdns" and supports_mdns: 

449 return run_list_mdns(error_cb, args) 

450 elif mode == "mdns-gui" and supports_mdns: 

451 check_gtk() 

452 return run_mdns_gui(error_cb, options) 

453 elif mode == "sessions": 

454 check_gtk() 

455 return run_sessions_gui(error_cb, options) 

456 elif mode == "launcher": 

457 check_gtk() 

458 from xpra.client.gtk_base.client_launcher import main as launcher_main 

459 return launcher_main(["xpra"]+args) 

460 elif mode == "gui": 

461 check_gtk() 

462 from xpra.gtk_common.gui import main as gui_main #@Reimport 

463 return gui_main() 

464 elif mode == "bug-report": 

465 check_gtk() 

466 from xpra.scripts.bug_report import main as bug_main #@Reimport 

467 bug_main(["xpra"]+args) 

468 elif ( 

469 mode=="_proxy" or 

470 (mode in ("_proxy_start", "_proxy_start_desktop") and supports_server) or 

471 (mode=="_proxy_shadow_start" and supports_shadow) 

472 ): 

473 nox() 

474 return run_proxy(error_cb, options, script_file, args, mode, defaults) 

475 elif mode in ("_sound_record", "_sound_play", "_sound_query"): 

476 if not has_sound_support(): 

477 error_cb("no sound support!") 

478 from xpra.sound.wrapper import run_sound 

479 return run_sound(mode, error_cb, options, args) 

480 elif mode=="_dialog": 

481 check_gtk() 

482 return run_dialog(args) 

483 elif mode=="_pass": 

484 check_gtk() 

485 return run_pass(args) 

486 elif mode=="send-file": 

487 check_gtk() 

488 return run_send_file(args) 

489 elif mode=="splash": 

490 check_gtk() 

491 return run_splash(args) 

492 elif mode=="opengl": 

493 check_gtk() 

494 return run_glcheck(options) 

495 elif mode=="opengl-probe": 

496 check_gtk() 

497 return run_glprobe(options) 

498 elif mode=="opengl-test": 

499 check_gtk() 

500 return run_glprobe(options, True) 

501 elif mode=="encoding": 

502 from xpra.codecs import loader 

503 return loader.main() 

504 elif mode=="video": 

505 from xpra.codecs import video_helper 

506 return video_helper.main() 

507 elif mode=="nvinfo": 

508 from xpra.codecs import nv_util 

509 return nv_util.main() 

510 elif mode=="webcam": 

511 check_gtk() 

512 from xpra.scripts import show_webcam 

513 return show_webcam.main() 

514 elif mode=="webcam-info": 

515 from xpra.platform import webcam 

516 webcam.main(args) 

517 elif mode=="clipboard-test": 

518 check_gtk() 

519 from xpra.gtk_common import gtk_view_clipboard 

520 return gtk_view_clipboard.main() 

521 elif mode=="keyboard": 

522 from xpra.platform import keyboard 

523 return keyboard.main() 

524 elif mode=="keyboard-test": 

525 check_gtk() 

526 from xpra.gtk_common import gtk_view_keyboard 

527 return gtk_view_keyboard.main() 

528 elif mode=="keymap": 

529 check_gtk() 

530 from xpra.gtk_common import keymap 

531 return keymap.main() 

532 elif mode=="gtk-info": 

533 check_gtk() 

534 from xpra.scripts import gtk_info 

535 return gtk_info.main() 

536 elif mode=="gui-info": 

537 check_gtk() 

538 from xpra.platform import gui 

539 return gui.main() 

540 elif mode=="network-info": 

541 from xpra.net import net_util 

542 return net_util.main() 

543 elif mode=="compression": 

544 from xpra.net import compression 

545 return compression.main() 

546 elif mode=="packet-encoding": 

547 from xpra.net import packet_encoding 

548 return packet_encoding.main() 

549 elif mode=="path-info": 

550 from xpra.platform import paths 

551 return paths.main() 

552 elif mode=="printing-info": 

553 from xpra.platform import printing 

554 return printing.main(args) 

555 elif mode=="version-info": 

556 from xpra.scripts import version 

557 return version.main() 

558 elif mode=="toolbox": 

559 check_gtk() 

560 from xpra.client.gtk_base import toolbox 

561 return toolbox.main() 

562 elif mode=="colors-test": 

563 check_gtk() 

564 from xpra.client.gtk_base.example import colors 

565 return colors.main() 

566 elif mode=="colors-gradient-test": 

567 check_gtk() 

568 from xpra.client.gtk_base.example import colors_gradient 

569 return colors_gradient.main() 

570 elif mode=="transparent-colors": 

571 check_gtk() 

572 from xpra.client.gtk_base.example import transparent_colors 

573 return transparent_colors.main() 

574 elif mode=="transparent-window": 

575 check_gtk() 

576 from xpra.client.gtk_base.example import transparent_window 

577 return transparent_window.main() 

578 elif mode == "initenv": 

579 if not POSIX: 

580 raise InitExit(EXIT_UNSUPPORTED, "initenv is not supported on this OS") 

581 from xpra.server.server_util import xpra_runner_shell_script, write_runner_shell_scripts 

582 script = xpra_runner_shell_script(script_file, os.getcwd(), options.socket_dir) 

583 write_runner_shell_scripts(script, False) 

584 return 0 

585 elif mode=="auth": 

586 return run_auth(options, args) 

587 elif mode == "showconfig": 

588 return run_showconfig(options, args) 

589 elif mode == "showsetting": 

590 return run_showsetting(options, args) 

591 else: 

592 from xpra.scripts.parsing import get_usage 

593 if mode!="help": 

594 print("Invalid subcommand '%s'" % (mode,)) 

595 print("Usage:") 

596 from xpra.platform.features import LOCAL_SERVERS_SUPPORTED 

597 if not LOCAL_SERVERS_SUPPORTED: 

598 print("(this xpra installation does not support starting local servers)") 

599 cmd = os.path.basename(sys.argv[0]) 

600 for x in get_usage(): 

601 print("\t%s %s" % (cmd, x)) 

602 print() 

603 print("see 'man xpra' or 'xpra --help' for more details") 

604 return 1 

605 

606 

607def find_session_by_name(opts, session_name): 

608 from xpra.platform.paths import get_nodock_command 

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

610 socket_paths = dotxpra.socket_paths(check_uid=getuid(), matching_state=DotXpra.LIVE) 

611 if not socket_paths: 

612 return None 

613 id_sessions = {} 

614 for socket_path in socket_paths: 

615 cmd = get_nodock_command()+["id", "socket://%s" % socket_path] 

616 proc = Popen(cmd, stdout=PIPE, stderr=PIPE) 

617 id_sessions[socket_path] = proc 

618 now = monotonic_time() 

619 import time 

620 while any(proc.poll() is None for proc in id_sessions.values()) and monotonic_time()-now<10: 

621 time.sleep(0.5) 

622 session_uuid_to_path = {} 

623 for socket_path, proc in id_sessions.items(): 

624 if proc.poll()==0: 

625 out, err = proc.communicate() 

626 d = {} 

627 for line in bytestostr(out or err).splitlines(): 

628 try: 

629 k,v = line.split("=", 1) 

630 d[k] = v 

631 except ValueError: 

632 continue 

633 name = d.get("session-name") 

634 uuid = d.get("uuid") 

635 if name==session_name and uuid: 

636 session_uuid_to_path[uuid] = socket_path 

637 if not session_uuid_to_path: 

638 return None 

639 if len(session_uuid_to_path)>1: 

640 raise InitException("more than one session found matching '%s'" % session_name) 

641 return "socket://%s" % tuple(session_uuid_to_path.values())[0] 

642 

643def parse_ssh_string(ssh_setting): 

644 ssh_cmd = shlex.split(ssh_setting, posix=not WIN32) 

645 if ssh_cmd[0]=="auto": 

646 #try paramiko: 

647 try: 

648 from xpra.net.ssh import nogssapi_context 

649 with nogssapi_context(): 

650 import paramiko 

651 assert paramiko 

652 ssh_cmd = ["paramiko"] 

653 if is_debug_enabled("ssh"): 

654 Logger("ssh").info("using paramiko ssh backend") 

655 except ImportError as e: 

656 if is_debug_enabled("ssh"): 

657 Logger("ssh").info("no paramiko: %s" % e) 

658 from xpra.platform.features import DEFAULT_SSH_COMMAND 

659 ssh_cmd = shlex.split(DEFAULT_SSH_COMMAND) 

660 return ssh_cmd 

661 

662def add_ssh_args(username, password, host, ssh_port, key, is_putty=False, is_paramiko=False): 

663 args = [] 

664 if password and is_putty: 

665 args += ["-pw", password] 

666 if username and not is_paramiko: 

667 args += ["-l", username] 

668 if ssh_port and ssh_port!=22: 

669 #grr why bother doing it different? 

670 if is_putty: 

671 args += ["-P", str(ssh_port)] 

672 elif not is_paramiko: 

673 args += ["-p", str(ssh_port)] 

674 if not is_paramiko: 

675 args += ["-T", host] 

676 if key: 

677 key_path = os.path.abspath(key) 

678 if WIN32 and is_putty: 

679 # tortoise plink works with either slash, backslash needs too much escaping 

680 # because of the weird way it's passed through as a ProxyCommand 

681 key_path = "\"" + key.replace("\\", "/") + "\"" # pragma: no cover 

682 args += ["-i", key_path] 

683 return args 

684 

685def add_ssh_proxy_args(username, password, host, ssh_port, pkey, ssh, is_putty=False, is_paramiko=False): 

686 args = [] 

687 proxyline = ssh 

688 if is_putty: 

689 proxyline += ["-nc", "%host:%port"] 

690 elif not is_paramiko: 

691 proxyline += ["-W", "%h:%p"] 

692 # the double quotes are in case the password has something like "&" 

693 proxyline += add_ssh_args(username, password, host, ssh_port, pkey, is_putty, is_paramiko) 

694 if is_putty: 

695 args += ["-proxycmd", " ".join(proxyline)] 

696 elif not is_paramiko: 

697 args += ["-o", "ProxyCommand " + " ".join(proxyline)] 

698 return args 

699 

700 

701def parse_proxy_attributes(display_name): 

702 import re 

703 # Notes: 

704 # (1) this regex permits a "?" in the password or username (because not just splitting at "?"). 

705 # It doesn't look for the next "?" until after the "@", where a "?" really indicates 

706 # another field. 

707 # (2) all characters including "@"s go to "userpass" until the *last* "@" after which it all goes 

708 # to "hostport" 

709 reout = re.search("\\?proxy=(?P<p>((?P<userpass>.+)@)?(?P<hostport>[^?]+))", display_name) 

710 if not reout: 

711 return display_name, {} 

712 try: 

713 desc_tmp = {} 

714 # This one should *always* return a host, and should end with an optional numeric port 

715 hostport = reout.group("hostport") 

716 hostport_match = re.match(r"(?P<host>[^:]+)($|:(?P<port>\d+)$)", hostport) 

717 if not hostport_match: 

718 raise RuntimeError("bad format for 'hostport': '%s'" % hostport) 

719 host = hostport_match.group("host") 

720 if not host: 

721 raise RuntimeError("bad format: missing host in '%s'" % hostport) 

722 desc_tmp["proxy_host"] = host 

723 if hostport_match.group("port"): 

724 try: 

725 desc_tmp["proxy_port"] = int(hostport_match.group("port")) 

726 except ValueError: 

727 raise RuntimeError("bad format: proxy port '%s' is not a number" % hostport_match.group("port")) 

728 userpass = reout.group("userpass") 

729 if userpass: 

730 # The username ends at the first colon. This decision was not unique: I could have 

731 # allowed one colon in username if there were two in the string. 

732 userpass_match = re.match("(?P<username>[^:]+)(:(?P<password>.+))?", userpass) 

733 if not userpass_match: 

734 raise RuntimeError("bad format for 'userpass': '%s'" % userpass) 

735 # If there is a "userpass" part, then it *must* have a username 

736 username = userpass_match.group("username") 

737 if not username: 

738 raise RuntimeError("bad format: missing username in '%s'" % userpass) 

739 desc_tmp["proxy_username"] = username 

740 password = userpass_match.group("password") 

741 if password: 

742 desc_tmp["proxy_password"] = password 

743 except RuntimeError: 

744 sshlog = Logger("ssh") 

745 sshlog.error("bad proxy argument: " + reout.group(0)) 

746 return display_name, {} 

747 else: 

748 # rip out the part we've processed 

749 display_name = display_name[:reout.start()] + display_name[reout.end():] 

750 return display_name, desc_tmp 

751 

752def parse_display_name(error_cb, opts, display_name, session_name_lookup=False): 

753 if WIN32: 

754 from xpra.platform.win32.dotxpra import PIPE_PREFIX # pragma: no cover 

755 else: 

756 PIPE_PREFIX = None 

757 if display_name.startswith("/") and POSIX: 

758 display_name = "socket://"+display_name 

759 desc = {"display_name" : display_name} 

760 display_name, proxy_attrs = parse_proxy_attributes(display_name) 

761 desc.update(proxy_attrs) 

762 

763 #split the display name on ":" or "/" 

764 scpos = display_name.find(":") 

765 slpos = display_name.find("/") 

766 if scpos<0 and slpos<0: 

767 match = None 

768 if POSIX: 

769 #maybe this is just the display number without the ":" prefix? 

770 try: 

771 display_name = ":%i" % int(display_name) 

772 match = True 

773 except ValueError: 

774 pass 

775 elif WIN32: # pragma: no cover 

776 display_name = "named-pipe://%s%s" % (PIPE_PREFIX, display_name) 

777 match = True 

778 if session_name_lookup and not match: 

779 #try to find a session whose "session-name" matches: 

780 match = find_session_by_name(opts, display_name) 

781 if match: 

782 display_name = match 

783 #display_name may have been updated, re-parse it: 

784 scpos = display_name.find(":") 

785 slpos = display_name.find("/") 

786 if scpos<0 and slpos<0: 

787 error_cb("unknown format for display name: %s" % display_name) 

788 if scpos<0: 

789 pos = slpos 

790 elif slpos<0: 

791 pos = scpos 

792 else: 

793 pos = min(scpos, slpos) 

794 protocol = display_name[:pos] 

795 #the separator between the protocol and the rest can be ":", "/" or "://" 

796 #but the separator value we use thereafter can only be ":" or "/" 

797 #because we want strings like ssl://host:port/DISPLAY to be parsed into ["ssl", "host:port", "DISPLAY"] 

798 psep = "" 

799 if display_name[pos]==":": 

800 psep += ":" 

801 pos += 1 

802 scount = 0 

803 while display_name[pos]=="/" and scount<2: 

804 psep += "/" 

805 pos += 1 

806 scount += 1 

807 if protocol=="socket": 

808 #socket paths may start with a slash! 

809 #so socket:/path means that the slash is part of the path 

810 if psep==":/": 

811 psep = psep[:-1] 

812 pos -= 1 

813 if psep not in (":", "/", "://"): 

814 error_cb("unknown format for protocol separator '%s' in display name: %s" % (psep, display_name)) 

815 afterproto = display_name[pos:] #ie: "host:port/DISPLAY" 

816 separator = psep[-1] #ie: "/" 

817 parts = afterproto.split(separator, 1) #ie: "host:port", "DISPLAY" 

818 

819 def parse_username_and_password(s): 

820 ppos = s.find(":") 

821 if ppos>=0: 

822 password = s[ppos+1:] 

823 username = s[:ppos] 

824 else: 

825 username = s 

826 password = "" 

827 #fugly: we override the command line option after parsing the string: 

828 if username: 

829 desc["username"] = username 

830 opts.username = username 

831 if password: 

832 opts.password = password 

833 desc["password"] = password 

834 return username, password 

835 

836 def parse_host_string(host, default_port=DEFAULT_PORT): 

837 """ 

838 Parses [username[:password]@]host[:port] 

839 and returns username, password, host, port 

840 missing arguments will be empty (username and password) or 0 (port) 

841 """ 

842 upos = host.rfind("@") 

843 username = None 

844 password = None 

845 port = default_port 

846 if upos>=0: 

847 #HOST=username@host 

848 username, password = parse_username_and_password(host[:upos]) 

849 host = host[upos+1:] 

850 port_str = None 

851 if host.count(":")>=2: 

852 #more than 2 ":", assume this is IPv6: 

853 if host.startswith("["): 

854 #if we have brackets, we can support: "[HOST]:SSHPORT" 

855 epos = host.find("]") 

856 if epos<0: 

857 error_cb("invalid host format, expected IPv6 [..]") 

858 port_str = host[epos+1:] #ie: ":22" 

859 if port_str.startswith(":"): 

860 port_str = port_str[1:] #ie: "22" 

861 host = host[1:epos] #ie: "[HOST]" 

862 else: 

863 #ie: fe80::c1:ac45:7351:ea69%eth1:14500 -> ["fe80::c1:ac45:7351:ea69", "eth1:14500"] 

864 devsep = host.split("%") 

865 if len(devsep)==2: 

866 parts = devsep[1].split(":", 1) #ie: "eth1:14500" -> ["eth1", "14500"] 

867 if len(parts)==2: 

868 host = "%s%%%s" % (devsep[0], parts[0]) 

869 port_str = parts[1] #ie: "14500" 

870 else: 

871 parts = host.split(":") 

872 if len(parts[-1])>4: 

873 port_str = parts[-1] 

874 host = ":".join(parts[:-1]) 

875 else: 

876 #otherwise, we have to assume they are all part of IPv6 

877 #we could count them at split at 8, but that would be just too fugly 

878 pass 

879 elif host.find(":")>0: 

880 host, port_str = host.split(":", 1) 

881 if port_str: 

882 try: 

883 port = int(port_str) 

884 except ValueError: 

885 error_cb("invalid port number specified: %s" % port_str) 

886 if port<=0 or port>=2**16: 

887 error_cb("invalid port number: %s" % port) 

888 desc["port"] = port 

889 if host=="": 

890 host = "127.0.0.1" 

891 desc["host"] = host 

892 desc["local"] = is_local(host) 

893 return username, password, host, port 

894 

895 def parse_remote_display(s): 

896 if not s: 

897 return 

898 qpos = s.find("?") 

899 cpos = s.find(",") 

900 display = None 

901 options_str = None 

902 if qpos>=0 and (qpos<cpos or cpos<0): 

903 #query string format, ie: "DISPLAY?key1=value1&key2=value2#extra_stuff 

904 attr_sep = "&" 

905 parts = s.split("?", 1) 

906 s = parts[0].split("#")[0] 

907 options_str = parts[1] 

908 elif cpos>0 and (cpos<qpos or qpos<0): 

909 #csv string format, 

910 # ie: DISPLAY,key1=value1,key2=value2 

911 # or: key1=value1,key2=value2 

912 attr_sep = "," 

913 parts = s.split(",", 1) 

914 if parts[0].find("=")>0: 

915 #if the first part is a key=value, 

916 #assume it is part of the parameters 

917 parts = ["", s] 

918 display = "" 

919 if len(parts)==2: 

920 options_str = parts[1] 

921 elif s.find("=")>0: 

922 #ie: just one key=value 

923 #(so this is not a display) 

924 display = "" 

925 attr_sep = "," 

926 parts = ["", s] 

927 else: 

928 parts = [] 

929 if display is None: 

930 try: 

931 assert [int(x) for x in s.split(".")] #ie: ":10.0" -> [10, 0] 

932 display = ":" + s #ie: ":10.0" 

933 except ValueError: 

934 display = s #ie: "tcp://somehost:10000/" 

935 desc["display"] = display 

936 opts.display = display 

937 desc["display_as_args"] = [display] 

938 if options_str: 

939 #parse extra attributes 

940 d = parse_simple_dict(options_str, attr_sep) 

941 for k,v in d.items(): 

942 if k in desc: 

943 warn("Warning: cannot override '%s' with URI" % k) 

944 else: 

945 desc[k] = v 

946 

947 if protocol=="ssh": 

948 desc.update({ 

949 "type" : "ssh", 

950 "proxy_command" : ["_proxy"], 

951 "exit_ssh" : opts.exit_ssh, 

952 }) 

953 desc["display"] = None 

954 desc["display_as_args"] = [] 

955 host = parts[0] 

956 if len(parts)>1: 

957 parse_remote_display(parts[1]) 

958 #ie: ssh=["/usr/bin/ssh", "-v"] 

959 ssh = parse_ssh_string(opts.ssh) 

960 full_ssh = ssh[:] 

961 

962 #maybe restrict to win32 only? 

963 ssh_cmd = ssh[0].lower() 

964 is_putty = ssh_cmd.endswith("plink") or ssh_cmd.endswith("plink.exe") 

965 is_paramiko = ssh_cmd.split(":")[0]=="paramiko" 

966 if is_paramiko: 

967 ssh[0] = "paramiko" 

968 desc["is_paramiko"] = is_paramiko 

969 if opts.ssh.find(":")>0: 

970 desc["paramiko-config"] = parse_simple_dict(opts.ssh.split(":", 1)[1]) 

971 if is_putty: 

972 desc["is_putty"] = True 

973 #special env used by plink: 

974 env = os.environ.copy() 

975 env["PLINK_PROTOCOL"] = "ssh" 

976 

977 username, password, host, ssh_port = parse_host_string(host, 22) 

978 if username: 

979 #TODO: let parse_host_string set it? 

980 desc["username"] = username 

981 opts.username = username 

982 if ssh_port and ssh_port!=22: 

983 desc["ssh-port"] = ssh_port 

984 key = desc.get("key", None) 

985 full_ssh += add_ssh_args(username, password, host, ssh_port, key, is_putty, is_paramiko) 

986 if "proxy_host" in desc: 

987 proxy_username = desc.get("proxy_username", "") 

988 proxy_password = desc.get("proxy_password", "") 

989 proxy_host = desc["proxy_host"] 

990 proxy_port = desc.get("proxy_port", 22) 

991 proxy_key = desc.get("proxy_key", "") 

992 full_ssh += add_ssh_proxy_args(proxy_username, proxy_password, proxy_host, proxy_port, 

993 proxy_key, ssh, is_putty, is_paramiko) 

994 desc.update({ 

995 "host" : host, 

996 "full_ssh" : full_ssh, 

997 "remote_xpra" : opts.remote_xpra, 

998 }) 

999 if opts.socket_dir: 

1000 desc["socket_dir"] = opts.socket_dir 

1001 if password is None and opts.password_file: 

1002 for x in opts.password_file: 

1003 if os.path.exists(x): 

1004 try: 

1005 with open(opts.password_file, "rb") as f: 

1006 desc["password"] = f.read() 

1007 break 

1008 except Exception as e: 

1009 warn("Error: failed to read the password file '%s':\n" % x) 

1010 warn(" %s\n" % e) 

1011 return desc 

1012 elif protocol=="socket": 

1013 assert not WIN32, "unix-domain sockets are not supported on MS Windows" 

1014 #use the socketfile specified: 

1015 slash = afterproto.find("/") 

1016 if 0<afterproto.find(":")<slash: 

1017 #ie: username:password/run/user/1000/xpra/hostname-number 

1018 #remove username and password prefix: 

1019 parse_username_and_password(afterproto[:slash]) 

1020 sockfile = afterproto[slash:] 

1021 elif afterproto.find("@")>=0: 

1022 #ie: username:password@/run/user/1000/xpra/hostname-number 

1023 parts = afterproto.split("@") 

1024 parse_username_and_password("@".join(parts[:-1])) 

1025 sockfile = parts[-1] 

1026 else: 

1027 sockfile = afterproto 

1028 desc.update({ 

1029 "type" : "unix-domain", 

1030 "local" : True, 

1031 "socket_dir" : os.path.basename(sockfile), 

1032 "socket_dirs" : opts.socket_dirs, 

1033 "socket_path" : sockfile, 

1034 }) 

1035 opts.display = None 

1036 return desc 

1037 elif display_name.startswith(":"): 

1038 assert not WIN32, "X11 display names are not supported on MS Windows" 

1039 desc.update({ 

1040 "type" : "unix-domain", 

1041 "local" : True, 

1042 "display" : display_name, 

1043 "socket_dirs" : opts.socket_dirs}) 

1044 opts.display = display_name 

1045 if opts.socket_dir: 

1046 desc["socket_dir"] = opts.socket_dir 

1047 return desc 

1048 elif protocol in ("tcp", "ssl", "udp", "ws", "wss"): 

1049 desc.update({ 

1050 "type" : protocol, 

1051 }) 

1052 if len(parts) not in (1, 2, 3): 

1053 error_cb("invalid %s connection string,\n" % protocol 

1054 +" use %s://[username[:password]@]host[:port][/display]\n" % protocol) 

1055 #display (optional): 

1056 if separator=="/" and len(parts)==2: 

1057 parse_remote_display(parts[-1]) 

1058 parts = parts[:-1] 

1059 host = ":".join(parts) 

1060 username, password, host, port = parse_host_string(host) 

1061 assert port>0, "no port specified in %s" % host 

1062 return desc 

1063 elif protocol=="vsock": 

1064 #use the vsock specified: 

1065 cid, iport = parse_vsock(parts[0]) 

1066 desc.update({ 

1067 "type" : "vsock", 

1068 "local" : False, 

1069 "display" : display_name, 

1070 "vsock" : (cid, iport), 

1071 }) 

1072 opts.display = display_name 

1073 return desc 

1074 elif WIN32 or display_name.startswith("named-pipe:"): # pragma: no cover 

1075 if afterproto.find("@")>=0: 

1076 parts = afterproto.split("@") 

1077 parse_username_and_password("@".join(parts[:-1])) 

1078 pipe_name = parts[-1] 

1079 else: 

1080 pipe_name = afterproto 

1081 if not pipe_name.startswith(PIPE_PREFIX): 

1082 pipe_name = "%s%s" % (PIPE_PREFIX, pipe_name) 

1083 desc.update({ 

1084 "type" : "named-pipe", 

1085 "local" : True, 

1086 "display" : "DISPLAY", 

1087 "named-pipe" : pipe_name, 

1088 }) 

1089 opts.display = display_name 

1090 return desc 

1091 else: 

1092 error_cb("unknown format for display name: %s" % display_name) 

1093 

1094def pick_display(error_cb, opts, extra_args): 

1095 if not extra_args: 

1096 # Pick a default server 

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

1098 dir_servers = dotxpra.socket_details(matching_state=DotXpra.LIVE) 

1099 try: 

1100 sockdir, display, sockpath = single_display_match(dir_servers, error_cb) 

1101 except: 

1102 if getuid()==0 and opts.system_proxy_socket: 

1103 display = ":PROXY" 

1104 sockdir = os.path.dirname(opts.system_proxy_socket) 

1105 sockpath = opts.system_proxy_socket 

1106 else: 

1107 raise 

1108 desc = { 

1109 "local" : True, 

1110 "display" : display, 

1111 "display_name" : display, 

1112 } 

1113 if WIN32: # pragma: no cover 

1114 desc.update({ 

1115 "type" : "named-pipe", 

1116 "named-pipe" : sockpath, 

1117 }) 

1118 else: 

1119 desc.update({ 

1120 "type" : "unix-domain", 

1121 "socket_dir" : sockdir, 

1122 "socket_path" : sockpath, 

1123 }) 

1124 return desc 

1125 if len(extra_args) == 1: 

1126 return parse_display_name(error_cb, opts, extra_args[0], session_name_lookup=True) 

1127 error_cb("too many arguments (%i): %s" % (len(extra_args), extra_args)) 

1128 return None 

1129 

1130def single_display_match(dir_servers, error_cb, nomatch="cannot find any live servers to connect to"): 

1131 #ie: {"/tmp" : [LIVE, "desktop-10", "/tmp/desktop-10"]} 

1132 #aggregate all the different locations: 

1133 allservers = [] 

1134 noproxy = [] 

1135 for sockdir, servers in dir_servers.items(): 

1136 for state, display, path in servers: 

1137 if state==DotXpra.LIVE: 

1138 allservers.append((sockdir, display, path)) 

1139 if not display.startswith(":proxy-"): 

1140 noproxy.append((sockdir, display, path)) 

1141 if not allservers: 

1142 error_cb(nomatch) 

1143 if len(allservers)>1: 

1144 #maybe the same server is available under multiple paths 

1145 displays = set(v[1] for v in allservers) 

1146 if len(displays)==1: 

1147 #they all point to the same display, use the first one: 

1148 allservers = allservers[:1] 

1149 if len(allservers)>1 and noproxy: 

1150 #try to ignore proxy instances: 

1151 displays = set(v[1] for v in noproxy) 

1152 if len(displays)==1: 

1153 #they all point to the same display, use the first one: 

1154 allservers = noproxy[:1] 

1155 if len(allservers) > 1: 

1156 error_cb("there are multiple servers running, please specify") 

1157 assert len(allservers)==1 

1158 sockdir, name, path = allservers[0] 

1159 #ie: ("/tmp", "desktop-10", "/tmp/desktop-10") 

1160 return sockdir, name, path 

1161 

1162 

1163def connect_or_fail(display_desc, opts): 

1164 from xpra.net.bytestreams import ConnectionClosedException 

1165 try: 

1166 return connect_to(display_desc, opts) 

1167 except ConnectionClosedException as e: 

1168 raise InitExit(EXIT_CONNECTION_FAILED, "%s" % e) from None 

1169 except InitException: 

1170 raise 

1171 except InitExit: 

1172 raise 

1173 except InitInfo: 

1174 raise 

1175 except Exception as e: 

1176 get_util_logger().debug("failed to connect", exc_info=True) 

1177 raise InitException("connection failed: %s" % e) from None 

1178 

1179 

1180def socket_connect(dtype, host, port): 

1181 if dtype=="udp": 

1182 socktype = socket.SOCK_DGRAM 

1183 else: 

1184 socktype = socket.SOCK_STREAM 

1185 family = 0 #any 

1186 try: 

1187 addrinfo = socket.getaddrinfo(host, port, family, socktype) 

1188 except Exception as e: 

1189 raise InitException("cannot get %s address of%s: %s" % ({ 

1190 socket.AF_INET6 : " IPv6", 

1191 socket.AF_INET : " IPv4", 

1192 }.get(family, ""), (host, port), e)) from None 

1193 retry = 0 

1194 start = monotonic_time() 

1195 while True: 

1196 #try each one: 

1197 for addr in addrinfo: 

1198 sockaddr = addr[-1] 

1199 family = addr[0] 

1200 sock = socket.socket(family, socktype) 

1201 if dtype!="udp": 

1202 from xpra.net.bytestreams import SOCKET_TIMEOUT 

1203 sock.settimeout(SOCKET_TIMEOUT) 

1204 log = Logger("network") 

1205 try: 

1206 log("socket.connect(%s)", sockaddr) 

1207 sock.connect(sockaddr) 

1208 sock.settimeout(None) 

1209 return sock 

1210 except Exception as e: 

1211 log("failed to connect using %s%s for %s", sock.connect, sockaddr, addr, exc_info=True) 

1212 noerr(sock.close) 

1213 if monotonic_time()-start>=CONNECT_TIMEOUT: 

1214 break 

1215 if retry==0: 

1216 werr("failed to connect to %s:%s, retrying for %i seconds" % (host, port, CONNECT_TIMEOUT)) 

1217 retry += 1 

1218 import time 

1219 time.sleep(1) 

1220 raise InitExit(EXIT_CONNECTION_FAILED, "failed to connect to %s:%s" % (host, port)) 

1221 

1222 

1223def get_host_target_string(display_desc, port_key="port", prefix=""): 

1224 dtype = display_desc["type"] 

1225 username = display_desc.get(prefix+"username") 

1226 host = display_desc[prefix+"host"] 

1227 try: 

1228 port = int(display_desc.get(prefix+port_key)) 

1229 if not 0<port<2**16: 

1230 port = 0 

1231 except (ValueError, TypeError): 

1232 port = 0 

1233 display = display_desc.get(prefix+"display", "") 

1234 return host_target_string(dtype, username, host, port, display) 

1235 

1236def host_target_string(dtype, username, host, port, display): 

1237 target = "%s://" % dtype 

1238 if username: 

1239 target += "%s@" % username 

1240 target += host 

1241 default_port = DEFAULT_PORTS.get(dtype, 0) 

1242 if port and port!=default_port: 

1243 target += ":%i" % port 

1244 if display and display.startswith(":"): 

1245 display = display[1:] 

1246 target += "/%s" % (display or "") 

1247 return target 

1248 

1249 

1250def connect_to(display_desc, opts=None, debug_cb=None, ssh_fail_cb=None): 

1251 from xpra.net.bytestreams import SOCKET_TIMEOUT, VSOCK_TIMEOUT, SocketConnection 

1252 display_name = display_desc["display_name"] 

1253 dtype = display_desc["type"] 

1254 if dtype == "ssh": 

1255 from xpra.net.ssh import ssh_paramiko_connect_to, ssh_exec_connect_to 

1256 if display_desc.get("is_paramiko", False): 

1257 return ssh_paramiko_connect_to(display_desc) 

1258 return ssh_exec_connect_to(display_desc, opts, debug_cb, ssh_fail_cb) 

1259 

1260 if dtype == "unix-domain": 

1261 if not hasattr(socket, "AF_UNIX"): # pragma: no cover 

1262 raise InitExit(EXIT_UNSUPPORTED, "unix domain sockets are not available on this operating system") 

1263 def sockpathfail_cb(msg): 

1264 raise InitException(msg) 

1265 sockpath = get_sockpath(display_desc, sockpathfail_cb) 

1266 display_desc["socket_path"] = sockpath 

1267 sock = socket.socket(socket.AF_UNIX) 

1268 sock.settimeout(SOCKET_TIMEOUT) 

1269 try: 

1270 sock.connect(sockpath) 

1271 except Exception as e: 

1272 get_util_logger().debug("failed to connect using %s%s", sock.connect, sockpath, exc_info=True) 

1273 noerr(sock.close) 

1274 raise InitExit(EXIT_CONNECTION_FAILED, "failed to connect to '%s':\n %s" % (sockpath, e)) from None 

1275 sock.settimeout(None) 

1276 conn = SocketConnection(sock, sock.getsockname(), sock.getpeername(), display_name, dtype) 

1277 conn.timeout = SOCKET_TIMEOUT 

1278 target = "socket://" 

1279 if display_desc.get("username"): 

1280 target += "%s@" % display_desc.get("username") 

1281 target += sockpath 

1282 conn.target = target 

1283 return conn 

1284 

1285 if dtype == "named-pipe": # pragma: no cover 

1286 pipe_name = display_desc["named-pipe"] 

1287 if not WIN32: 

1288 raise InitException("named pipes are only supported on MS Windows") 

1289 import errno 

1290 from xpra.platform.win32.dotxpra import PIPE_PATH, PIPE_ROOT 

1291 from xpra.platform.win32.namedpipes.connection import NamedPipeConnection, connect_to_namedpipe 

1292 if pipe_name.startswith(PIPE_ROOT): 

1293 #absolute pipe path already specified 

1294 path = pipe_name 

1295 else: 

1296 path = PIPE_PATH+pipe_name 

1297 try: 

1298 pipe_handle = connect_to_namedpipe(path) 

1299 except Exception as e: 

1300 try: 

1301 if e.args[0]==errno.ENOENT: 

1302 raise InitException("the named pipe '%s' does not exist: %s" % (pipe_name, e)) from None 

1303 except AttributeError: 

1304 pass 

1305 raise InitException("failed to connect to the named pipe '%s':\n %s" % (pipe_name, e)) from None 

1306 conn = NamedPipeConnection(pipe_name, pipe_handle, {}) 

1307 conn.timeout = SOCKET_TIMEOUT 

1308 conn.target = "namedpipe://%s/" % pipe_name 

1309 return conn 

1310 

1311 if dtype == "vsock": 

1312 cid, iport = display_desc["vsock"] 

1313 from xpra.net.vsock import ( #pylint: disable=no-name-in-module 

1314 connect_vsocket, #@UnresolvedImport 

1315 CID_TYPES, CID_ANY, PORT_ANY, #@UnresolvedImport 

1316 ) 

1317 sock = connect_vsocket(cid=cid, port=iport) 

1318 sock.timeout = VSOCK_TIMEOUT 

1319 sock.settimeout(None) 

1320 conn = SocketConnection(sock, "local", "host", (CID_TYPES.get(cid, cid), iport), dtype) 

1321 conn.target = "vsock://%s:%s" % ( 

1322 "any" if cid==CID_ANY else cid, 

1323 "any" if iport==PORT_ANY else iport, 

1324 ) 

1325 return conn 

1326 

1327 if dtype in ("tcp", "ssl", "ws", "wss", "udp"): 

1328 host = display_desc["host"] 

1329 port = display_desc["port"] 

1330 sock = socket_connect(dtype, host, port) 

1331 sock.settimeout(None) 

1332 conn = SocketConnection(sock, sock.getsockname(), sock.getpeername(), display_name, dtype, socket_options=display_desc) 

1333 

1334 if dtype=="udp": 

1335 #mmap mode requires all packets to be received, 

1336 #so we can free up the mmap chunks regularly 

1337 opts.mmap = False 

1338 if dtype in ("ssl", "wss"): 

1339 strict_host_check = display_desc.get("strict-host-check") 

1340 if strict_host_check is False: 

1341 opts.ssl_server_verify_mode = "none" 

1342 from xpra.net.socket_util import ssl_wrap_socket, get_ssl_attributes 

1343 if not opts.ssl_server_hostname: 

1344 #if the server hostname was not specified explicitly, 

1345 #use the one from the connection string: 

1346 opts.ssl_server_hostname = host 

1347 kwargs = get_ssl_attributes(opts, server_side=False, overrides=display_desc) 

1348 sock = ssl_wrap_socket(sock, **kwargs) 

1349 assert sock, "failed to wrap socket %s" % sock 

1350 conn._socket = sock 

1351 conn.timeout = SOCKET_TIMEOUT 

1352 

1353 #wrap in a websocket: 

1354 if dtype in ("ws", "wss"): 

1355 host = display_desc["host"] 

1356 port = display_desc.get("port", 0) 

1357 #do the websocket upgrade and switch to binary 

1358 try: 

1359 from xpra.net.websockets.common import client_upgrade 

1360 except ImportError as e: # pragma: no cover 

1361 raise InitExit(EXIT_UNSUPPORTED, "cannot handle websocket connection: %s" % e) 

1362 else: 

1363 client_upgrade(conn.read, conn.write, host, port) 

1364 conn.target = get_host_target_string(display_desc) 

1365 return conn 

1366 raise InitException("unsupported display type: %s" % dtype) 

1367 

1368 

1369 

1370def run_dialog(extra_args): 

1371 from xpra.client.gtk_base.confirm_dialog import show_confirm_dialog 

1372 return show_confirm_dialog(extra_args) 

1373 

1374def run_pass(extra_args): 

1375 from xpra.client.gtk_base.pass_dialog import show_pass_dialog 

1376 return show_pass_dialog(extra_args) 

1377 

1378def run_send_file(extra_args): 

1379 sockpath = os.environ.get("XPRA_SERVER_SOCKET") 

1380 if not sockpath: 

1381 display = os.environ.get("DISPLAY") 

1382 if display: 

1383 uri = "%s" % display 

1384 else: 

1385 raise InitException("cannot find xpra server to use") 

1386 else: 

1387 uri = "socket://%s" % sockpath 

1388 if extra_args: 

1389 files = extra_args 

1390 else: 

1391 from xpra.gtk_common.gtk_util import choose_files 

1392 files = choose_files(None, "Select Files to Send", multiple=True) 

1393 if not files: 

1394 return 

1395 filelog = Logger("file") 

1396 import subprocess 

1397 from xpra.platform.paths import get_xpra_command 

1398 xpra_cmd = get_xpra_command() 

1399 errors = 0 

1400 for f in files: 

1401 filelog("run_send_file(%s) sending '%s'", extra_args, f) 

1402 if not os.path.isabs(f): 

1403 f = os.path.abspath(f) 

1404 #xpra control :10 send-file /path/to/the-file-to-send open CLIENT_UUID 

1405 cmd = xpra_cmd + ["control", uri, "send-file", f] 

1406 filelog("cmd=%s", cmd) 

1407 proc = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

1408 stdout, stderr = proc.communicate() 

1409 if proc.returncode: 

1410 filelog.error("Error: failed to send file '%s'", f) 

1411 if stdout: 

1412 filelog.error(" %s", stdout.decode()) 

1413 if stderr: 

1414 filelog.error(" %s", stderr) 

1415 errors += 1 

1416 else: 

1417 filelog.info("sent '%s'", f) 

1418 if errors: 

1419 return EXIT_FAILURE 

1420 return 0 

1421 

1422def get_sockpath(display_desc, error_cb, timeout=CONNECT_TIMEOUT): 

1423 #if the path was specified, use that: 

1424 sockpath = display_desc.get("socket_path") 

1425 if not sockpath: 

1426 #find the socket using the display: 

1427 dotxpra = DotXpra( 

1428 display_desc.get("socket_dir"), 

1429 display_desc.get("socket_dirs"), 

1430 display_desc.get("username", ""), 

1431 display_desc.get("uid", 0), 

1432 display_desc.get("gid", 0), 

1433 ) 

1434 display = display_desc["display"] 

1435 def socket_details(state=DotXpra.LIVE): 

1436 return dotxpra.socket_details(matching_state=state, matching_display=display) 

1437 dir_servers = socket_details() 

1438 if display and not dir_servers: 

1439 state = dotxpra.get_display_state(display) 

1440 if state in (DotXpra.UNKNOWN, DotXpra.DEAD) and timeout>0: 

1441 #found the socket for this specific display in UNKNOWN state, 

1442 #or not found any sockets at all (DEAD), 

1443 #this could be a server starting up, 

1444 #so give it a bit of time: 

1445 if state==DotXpra.UNKNOWN: 

1446 werr("server socket for display %s is in %s state" % (display, DotXpra.UNKNOWN)) 

1447 else: 

1448 werr("server socket for display %s not found" % display) 

1449 werr(" waiting up to %i seconds" % timeout) 

1450 start = monotonic_time() 

1451 log = Logger("network") 

1452 while monotonic_time()-start<timeout: 

1453 state = dotxpra.get_display_state(display) 

1454 log("get_display_state(%s)=%s", display, state) 

1455 if state in (dotxpra.LIVE, dotxpra.INACCESSIBLE): 

1456 #found a final state 

1457 break 

1458 import time 

1459 time.sleep(0.1) 

1460 dir_servers = socket_details() 

1461 sockpath = single_display_match(dir_servers, error_cb, 

1462 nomatch="cannot find live server for display %s" % display)[-1] 

1463 return sockpath 

1464 

1465def run_client(error_cb, opts, extra_args, mode): 

1466 if mode=="attach": 

1467 check_gtk() 

1468 if mode in ("attach", "detach") and len(extra_args)==1 and extra_args[0]=="all": 

1469 #run this command for each display: 

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

1471 displays = dotxpra.displays(check_uid=getuid(), matching_state=DotXpra.LIVE) 

1472 if not displays: 

1473 sys.stdout.write("No xpra sessions found\n") 

1474 return 1 

1475 #we have to locate the 'all' command line argument, 

1476 #so we can replace it with each display we find, 

1477 #but some other command line arguments can take a value of 'all', 

1478 #so we have to make sure that the one we find does not belong to the argument before 

1479 index = None 

1480 for i, arg in enumerate(sys.argv): 

1481 if i==0 or arg!="all": 

1482 continue 

1483 prevarg = sys.argv[i-1] 

1484 if prevarg[0]=="-" and (prevarg.find("=")<0 or len(prevarg)==2): 

1485 #ie: [.., "--csc-modules", "all"] or [.., "-d", "all"] 

1486 continue 

1487 index = i 

1488 break 

1489 if not index: 

1490 raise InitException("'all' command line argument could not be located") 

1491 cmd = sys.argv[:index]+sys.argv[index+1:] 

1492 for display in displays: 

1493 dcmd = cmd + [display] + ["--splash=no"] 

1494 Popen(dcmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=not WIN32) 

1495 return 0 

1496 app = get_client_app(error_cb, opts, extra_args, mode) 

1497 return do_run_client(app) 

1498 

1499 

1500def connect_to_server(app, display_desc, opts): 

1501 #on win32, we must run the main loop 

1502 #before we can call connect() 

1503 #because connect() may run a subprocess, 

1504 #and Gdk locks up the system if the main loop is not running by then! 

1505 from gi.repository import GLib 

1506 log = Logger("network") 

1507 def do_setup_connection(): 

1508 try: 

1509 log("do_setup_connection() display_desc=%s", display_desc) 

1510 conn = connect_or_fail(display_desc, opts) 

1511 log("do_setup_connection() conn=%s", conn) 

1512 #UGLY warning: connect_or_fail() will parse the display string, 

1513 #which may change the username and password.. 

1514 app.username = opts.username 

1515 app.password = opts.password 

1516 app.display = opts.display 

1517 app.display_desc = display_desc 

1518 protocol = app.setup_connection(conn) 

1519 protocol.start() 

1520 except InitInfo as e: 

1521 log("do_setup_connection() display_desc=%s", display_desc, exc_info=True) 

1522 werr("failed to connect:", " %s" % e) 

1523 GLib.idle_add(app.quit, EXIT_OK) 

1524 except InitExit as e: 

1525 log("do_setup_connection() display_desc=%s", display_desc, exc_info=True) 

1526 werr("Warning: failed to connect:", " %s" % e) 

1527 GLib.idle_add(app.quit, e.status) 

1528 except InitException as e: 

1529 log("do_setup_connection() display_desc=%s", display_desc, exc_info=True) 

1530 werr("Warning: failed to connect:", " %s" % e) 

1531 GLib.idle_add(app.quit, EXIT_CONNECTION_FAILED) 

1532 except Exception as e: 

1533 log.error("do_setup_connection() display_desc=%s", display_desc, exc_info=True) 

1534 werr("Error: failed to connect:", " %s", e) 

1535 GLib.idle_add(app.quit, EXIT_CONNECTION_FAILED) 

1536 def setup_connection(): 

1537 log("setup_connection() starting setup-connection thread") 

1538 from xpra.make_thread import start_thread 

1539 start_thread(do_setup_connection, "setup-connection", True) 

1540 GLib.idle_add(setup_connection) 

1541 

1542 

1543def get_client_app(error_cb, opts, extra_args, mode): 

1544 validate_encryption(opts) 

1545 if mode=="screenshot": 

1546 if not extra_args: 

1547 error_cb("invalid number of arguments for screenshot mode") 

1548 screenshot_filename = extra_args[0] 

1549 extra_args = extra_args[1:] 

1550 

1551 request_mode = None 

1552 if mode in ("request-start", "request-start-desktop", "request-shadow"): 

1553 request_mode = mode.replace("request-", "") 

1554 

1555 try: 

1556 from xpra import client 

1557 assert client 

1558 except ImportError: 

1559 error_cb("Xpra client is not installed") 

1560 

1561 if opts.compression_level < 0 or opts.compression_level > 9: 

1562 error_cb("Compression level must be between 0 and 9 inclusive.") 

1563 if opts.quality!=-1 and (opts.quality < 0 or opts.quality > 100): 

1564 error_cb("Quality must be between 0 and 100 inclusive. (or -1 to disable)") 

1565 

1566 if mode=="screenshot": 

1567 from xpra.client.gobject_client_base import ScreenshotXpraClient 

1568 app = ScreenshotXpraClient(opts, screenshot_filename) 

1569 elif mode=="info": 

1570 from xpra.client.gobject_client_base import InfoXpraClient 

1571 app = InfoXpraClient(opts) 

1572 elif mode=="id": 

1573 from xpra.client.gobject_client_base import IDXpraClient 

1574 app = IDXpraClient(opts) 

1575 elif mode=="connect-test": 

1576 from xpra.client.gobject_client_base import ConnectTestXpraClient 

1577 app = ConnectTestXpraClient(opts) 

1578 elif mode=="_monitor": 

1579 from xpra.client.gobject_client_base import MonitorXpraClient 

1580 app = MonitorXpraClient(opts) 

1581 elif mode=="shell": 

1582 from xpra.client.gobject_client_base import ShellXpraClient 

1583 app = ShellXpraClient(opts) 

1584 elif mode=="control": 

1585 from xpra.client.gobject_client_base import ControlXpraClient 

1586 if len(extra_args)<=1: 

1587 error_cb("not enough arguments for 'control' mode, try 'help'") 

1588 args = extra_args[1:] 

1589 extra_args = extra_args[:1] 

1590 app = ControlXpraClient(opts) 

1591 app.set_command_args(args) 

1592 elif mode=="print": 

1593 from xpra.client.gobject_client_base import PrintClient 

1594 if len(extra_args)<=1: 

1595 error_cb("not enough arguments for 'print' mode") 

1596 args = extra_args[1:] 

1597 extra_args = extra_args[:1] 

1598 app = PrintClient(opts) 

1599 app.set_command_args(args) 

1600 elif mode=="qrcode": 

1601 check_gtk() 

1602 from xpra.client.gtk3.qrcode_client import QRCodeClient 

1603 app = QRCodeClient(opts) 

1604 elif mode=="version": 

1605 from xpra.client.gobject_client_base import VersionXpraClient 

1606 app = VersionXpraClient(opts) 

1607 elif mode=="detach": 

1608 from xpra.client.gobject_client_base import DetachXpraClient 

1609 app = DetachXpraClient(opts) 

1610 elif request_mode and opts.attach is not True: 

1611 from xpra.client.gobject_client_base import RequestStartClient 

1612 sns = get_start_new_session_dict(opts, request_mode, extra_args) 

1613 extra_args = ["socket:%s" % opts.system_proxy_socket] 

1614 app = RequestStartClient(opts) 

1615 app.hello_extra = {"connect" : False} 

1616 app.start_new_session = sns 

1617 else: 

1618 app = get_client_gui_app(error_cb, opts, request_mode, extra_args, mode) 

1619 try: 

1620 if mode!="listen": 

1621 app.show_progress(60, "connecting to server") 

1622 display_desc = pick_display(error_cb, opts, extra_args) 

1623 connect_to_server(app, display_desc, opts) 

1624 except Exception: 

1625 app.cleanup() 

1626 raise 

1627 return app 

1628 

1629 

1630def get_client_gui_app(error_cb, opts, request_mode, extra_args, mode): 

1631 check_gtk() 

1632 try: 

1633 app = make_client(error_cb, opts) 

1634 except RuntimeError as e: 

1635 #exceptions at this point are still initialization exceptions 

1636 raise InitException(e.args[0]) from None 

1637 ehelp = "help" in opts.encodings 

1638 if ehelp: 

1639 from xpra.codecs.codec_constants import PREFERRED_ENCODING_ORDER 

1640 opts.encodings = PREFERRED_ENCODING_ORDER 

1641 app.show_progress(30, "client configuration") 

1642 try: 

1643 app.init(opts) 

1644 if opts.encoding=="auto": 

1645 opts.encoding = "" 

1646 if opts.encoding or ehelp: 

1647 err = opts.encoding and (opts.encoding not in app.get_encodings()) 

1648 einfo = "" 

1649 if err and opts.encoding!="help": 

1650 einfo = "invalid encoding: %s\n" % opts.encoding 

1651 if opts.encoding=="help" or ehelp or err: 

1652 from xpra.codecs.loader import encodings_help 

1653 encodings = ["auto"] + app.get_encodings() 

1654 raise InitInfo(einfo+"%s xpra client supports the following encodings:\n * %s" % 

1655 (app.client_toolkit(), "\n * ".join(encodings_help(encodings)))) 

1656 def handshake_complete(*_args): 

1657 app.show_progress(100, "connection established") 

1658 log = get_util_logger() 

1659 try: 

1660 conn = app._protocol._conn 

1661 if conn: 

1662 log.info("Attached to %s", conn.target) 

1663 log.info(" (press Control-C to detach)\n") 

1664 except AttributeError: 

1665 return 

1666 if hasattr(app, "after_handshake"): 

1667 app.after_handshake(handshake_complete) 

1668 app.show_progress(40, "loading user interface") 

1669 app.init_ui(opts) 

1670 if request_mode: 

1671 sns = get_start_new_session_dict(opts, request_mode, extra_args) 

1672 extra_args = ["socket:%s" % opts.system_proxy_socket] 

1673 app.hello_extra = { 

1674 "start-new-session" : sns, 

1675 "connect" : True, 

1676 } 

1677 #we have consumed the start[-child] options 

1678 app.start_child_new_commands = [] 

1679 app.start_new_commands = [] 

1680 

1681 if mode=="listen": 

1682 if extra_args: 

1683 raise InitException("cannot specify extra arguments with 'listen' mode") 

1684 app.show_progress(80, "listening for incoming connections") 

1685 from xpra.platform import get_username 

1686 from xpra.net.socket_util import ( 

1687 get_network_logger, setup_local_sockets, peek_connection, 

1688 create_sockets, add_listen_socket, accept_connection, 

1689 ) 

1690 sockets = create_sockets(opts, error_cb) 

1691 #we don't have a display, 

1692 #so we can't automatically create sockets: 

1693 if "auto" in opts.bind: 

1694 opts.bind.remove("auto") 

1695 local_sockets = setup_local_sockets(opts.bind, 

1696 opts.socket_dir, opts.socket_dirs, 

1697 None, False, 

1698 opts.mmap_group, opts.socket_permissions, 

1699 get_username(), getuid, getgid) 

1700 sockets.update(local_sockets) 

1701 listen_cleanup = [] 

1702 socket_cleanup = [] 

1703 def new_connection(socktype, sock, handle=0): 

1704 from xpra.make_thread import start_thread 

1705 netlog = get_network_logger() 

1706 netlog("new_connection%s", (socktype, sock, handle)) 

1707 conn = accept_connection(socktype, sock) 

1708 #start a thread so we can sleep in peek_connection: 

1709 start_thread(handle_new_connection, "handle new connection: %s" % conn, daemon=True, args=(conn, )) 

1710 return True 

1711 def handle_new_connection(conn): 

1712 #see if this is a redirection: 

1713 netlog = get_network_logger() 

1714 line1 = peek_connection(conn)[1] 

1715 netlog("handle_new_connection(%s) line1=%s", conn, line1) 

1716 if line1: 

1717 from xpra.net.common import SOCKET_TYPES 

1718 uri = bytestostr(line1) 

1719 for socktype in SOCKET_TYPES: 

1720 if uri.startswith("%s://" % socktype): 

1721 run_socket_cleanups() 

1722 netlog.info("connecting to %s", uri) 

1723 extra_args[:] = [uri, ] 

1724 display_desc = pick_display(error_cb, opts, [uri, ]) 

1725 connect_to_server(app, display_desc, opts) 

1726 #app._protocol.start() 

1727 return 

1728 app.idle_add(do_handle_connection, conn) 

1729 def do_handle_connection(conn): 

1730 protocol = app.setup_connection(conn) 

1731 protocol.start() 

1732 #stop listening for new connections: 

1733 run_socket_cleanups() 

1734 def run_socket_cleanups(): 

1735 for cleanup in listen_cleanup: 

1736 cleanup() 

1737 listen_cleanup[:] = [] 

1738 #close the sockets: 

1739 for cleanup in socket_cleanup: 

1740 cleanup() 

1741 socket_cleanup[:] = [] 

1742 for socktype, sock, sinfo, cleanup_socket in sockets: 

1743 socket_cleanup.append(cleanup_socket) 

1744 cleanup = add_listen_socket(socktype, sock, sinfo, new_connection) 

1745 if cleanup: 

1746 listen_cleanup.append(cleanup) 

1747 #listen mode is special, 

1748 #don't fall through to connect_to_server! 

1749 app.show_progress(90, "ready") 

1750 return app 

1751 except Exception as e: 

1752 app.show_progress(100, "failure: %s" % e) 

1753 may_notify = getattr(app, "may_notify", None) 

1754 if may_notify: 

1755 from xpra.util import XPRA_FAILURE_NOTIFICATION_ID 

1756 body = str(e) 

1757 if body.startswith("failed to connect to"): 

1758 lines = body.split("\n") 

1759 summary = "Xpra client %s" % lines[0] 

1760 body = "\n".join(lines[1:]) 

1761 else: 

1762 summary = "Xpra client failed to connect" 

1763 may_notify(XPRA_FAILURE_NOTIFICATION_ID, summary, body, icon_name="disconnected") #pylint: disable=not-callable 

1764 app.cleanup() 

1765 raise 

1766 return app 

1767 

1768 

1769def make_progress_process(): 

1770 #start the splash subprocess 

1771 from xpra.platform.paths import get_nodock_command 

1772 cmd = get_nodock_command()+["splash"] 

1773 progress_process = Popen(cmd, stdin=PIPE) 

1774 def progress(pct, text): 

1775 if progress_process.poll(): 

1776 return 

1777 progress_process.stdin.write(("%i:%s\n" % (pct, text)).encode("latin1")) 

1778 progress_process.stdin.flush() 

1779 progress(10, "initializing") 

1780 return progress_process 

1781 

1782 

1783def run_opengl_probe(): 

1784 from xpra.platform.paths import get_nodock_command 

1785 log = Logger("opengl") 

1786 cmd = get_nodock_command()+["opengl"] 

1787 env = os.environ.copy() 

1788 if is_debug_enabled("opengl"): 

1789 cmd += ["-d", "opengl"] 

1790 else: 

1791 env["NOTTY"] = "1" 

1792 env["XPRA_HIDE_DOCK"] = "1" 

1793 env["XPRA_REDIRECT_OUTPUT"] = "0" 

1794 start = monotonic_time() 

1795 try: 

1796 proc = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env) 

1797 except Exception as e: 

1798 log.warn("Warning: failed to execute OpenGL probe command") 

1799 log.warn(" %s", e) 

1800 return "failed", {"message" : str(e).replace("\n", " ")} 

1801 try: 

1802 stdout, stderr = proc.communicate(timeout=OPENGL_PROBE_TIMEOUT) 

1803 r = proc.returncode 

1804 except TimeoutExpired: 

1805 log("opengl probe command timed out") 

1806 proc.kill() 

1807 stdout, stderr = proc.communicate() 

1808 r = None 

1809 log("xpra opengl stdout=%r", stdout) 

1810 log("xpra opengl stderr=%r", stderr) 

1811 log("OpenGL probe command returned %s for command=%s", r, cmd) 

1812 end = monotonic_time() 

1813 log("probe took %ims", 1000*(end-start)) 

1814 props = {} 

1815 for line in stdout.decode().splitlines(): 

1816 parts = line.split("=", 1) 

1817 if len(parts)==2: 

1818 props[parts[0]] = parts[1] 

1819 log("parsed OpenGL properties=%s", props) 

1820 def probe_message(): 

1821 err = props.get("error", "") 

1822 msg = props.get("message", "") 

1823 if err: 

1824 return "error:%s" % (err or msg) 

1825 if r==1: 

1826 return "crash" 

1827 if r is None: 

1828 return "timeout" 

1829 if r>128: 

1830 return "failed:%s" % SIGNAMES.get(r-128) 

1831 if r!=0: 

1832 return "failed:%s" % SIGNAMES.get(0-r, 0-r) 

1833 if props.get("success", "False").lower() in FALSE_OPTIONS: 

1834 from xpra.scripts.config import is_VirtualBox 

1835 if is_VirtualBox(): 

1836 return "error:incomplete OpenGL support in VirtualBox" 

1837 return "error:%s" % (err or msg) 

1838 if props.get("safe", "False").lower() in FALSE_OPTIONS: 

1839 return "warning:%s" % (err or msg) 

1840 return "success" 

1841 #log.warn("Warning: OpenGL probe failed: %s", msg) 

1842 return probe_message(), props 

1843 

1844def make_client(error_cb, opts): 

1845 progress_process = None 

1846 if opts.splash is not False: 

1847 progress_process = make_progress_process() 

1848 

1849 try: 

1850 from xpra.platform.gui import init as gui_init 

1851 gui_init() 

1852 

1853 def b(v): 

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

1855 def bo(v): 

1856 return str(v).lower() not in FALSE_OPTIONS or str(v).lower() in OFF_OPTIONS 

1857 impwarned = [] 

1858 def impcheck(*modules): 

1859 for mod in modules: 

1860 try: 

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

1862 except ImportError: 

1863 if mod not in impwarned: 

1864 impwarned.append(mod) 

1865 log = get_util_logger() 

1866 log("impcheck%s", modules, exc_info=True) 

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

1868 return False 

1869 return True 

1870 from xpra.client import mixin_features 

1871 mixin_features.display = opts.windows 

1872 mixin_features.windows = opts.windows 

1873 mixin_features.audio = (bo(opts.speaker) or bo(opts.microphone)) and impcheck("sound") 

1874 mixin_features.webcam = bo(opts.webcam) and impcheck("codecs") 

1875 mixin_features.clipboard = b(opts.clipboard) and impcheck("clipboard") 

1876 mixin_features.notifications = opts.notifications and impcheck("notifications") 

1877 mixin_features.dbus = opts.dbus_proxy and impcheck("dbus") 

1878 mixin_features.mmap = b(opts.mmap) 

1879 mixin_features.logging = b(opts.remote_logging) 

1880 mixin_features.tray = b(opts.tray) 

1881 mixin_features.network_state = True 

1882 mixin_features.network_listener = envbool("XPRA_CLIENT_BIND_SOCKETS", True) 

1883 mixin_features.encoding = opts.windows 

1884 from xpra.client.gtk3.client import XpraClient 

1885 app = XpraClient() 

1886 app.progress_process = progress_process 

1887 

1888 if opts.opengl=="probe": 

1889 if os.environ.get("XDG_SESSION_TYPE")=="wayland": 

1890 Logger("opengl").debug("wayland session detected, OpenGL disabled") 

1891 opts.opengl = "no" 

1892 else: 

1893 app.show_progress(20, "validating OpenGL configuration") 

1894 probe, info = run_opengl_probe() 

1895 opts.opengl = "probe-%s" % probe 

1896 r = probe #ie: "success" 

1897 if info: 

1898 renderer = info.get("renderer") 

1899 if renderer: 

1900 r += " (%s)" % renderer 

1901 app.show_progress(20, "validating OpenGL: %s" % r) 

1902 if probe=="error": 

1903 message = info.get("message") 

1904 if message: 

1905 app.show_progress(21, " %s" % message) 

1906 except Exception: 

1907 if progress_process: 

1908 try: 

1909 progress_process.terminate() 

1910 except Exception: 

1911 pass 

1912 raise 

1913 return app 

1914 

1915 

1916def do_run_client(app): 

1917 try: 

1918 return app.run() 

1919 except KeyboardInterrupt: 

1920 return -signal.SIGINT 

1921 finally: 

1922 app.cleanup() 

1923 

1924 

1925def get_start_new_session_dict(opts, mode, extra_args) -> dict: 

1926 sns = { 

1927 "mode" : mode, #ie: "start-desktop" 

1928 } 

1929 if len(extra_args)==1: 

1930 sns["display"] = extra_args[0] 

1931 from xpra.scripts.config import dict_to_config 

1932 defaults = dict_to_config(get_defaults()) 

1933 fixup_options(defaults) 

1934 for x in PROXY_START_OVERRIDABLE_OPTIONS: 

1935 fn = x.replace("-", "_") 

1936 v = getattr(opts, fn) 

1937 dv = getattr(defaults, fn, None) 

1938 if v and v!=dv: 

1939 sns[x] = v 

1940 #make sure the server will start in the same path we were called from: 

1941 #(despite being started by a root owned process from a different directory) 

1942 if not opts.chdir: 

1943 sns["chdir"] = os.getcwd() 

1944 return sns 

1945 

1946def shellquote(s : str) -> str: 

1947 return '"' + s.replace('"', '\\"') + '"' 

1948 

1949def strip_defaults_start_child(start_child, defaults_start_child): 

1950 if start_child and defaults_start_child: 

1951 #ensure we don't pass start / start-child commands 

1952 #which came from defaults (the configuration files) 

1953 #only the ones specified on the command line: 

1954 #(and only remove them once so the command line can re-add the same ones!) 

1955 for x in defaults_start_child: 

1956 if x in start_child: 

1957 start_child.remove(x) 

1958 return start_child 

1959 

1960 

1961def run_server(script_file, error_cb, options, args, mode, defaults): 

1962 display = None 

1963 display_is_remote = isdisplaytype(args, "ssh", "tcp", "ssl", "vsock") 

1964 if mode in ("start", "start-desktop") and args and parse_bool("attach", options.attach) is True: 

1965 check_gtk() 

1966 assert not display_is_remote 

1967 #maybe the server is already running 

1968 #and we don't need to bother trying to start it: 

1969 try: 

1970 display = pick_display(error_cb, options, args) 

1971 except Exception: 

1972 pass 

1973 else: 

1974 dotxpra = DotXpra(options.socket_dir, options.socket_dirs) 

1975 display_name = display.get("display_name") 

1976 if display_name: 

1977 state = dotxpra.get_display_state(display_name) 

1978 if state==DotXpra.LIVE: 

1979 noerr(sys.stdout.write, "existing live display found, attaching") 

1980 return do_run_mode(script_file, error_cb, options, args, "attach", defaults) 

1981 

1982 if ( 

1983 mode in ("start", "start-desktop", "upgrade", "upgrade-desktop") and not supports_server 

1984 ) or ( 

1985 mode=="shadow" and not supports_shadow 

1986 ) or ( 

1987 mode=="proxy" and not supports_proxy 

1988 ): 

1989 raise InitException("%s is not supported by this local installation" % mode) 

1990 

1991 if mode in ("start", "start-desktop") and args and parse_bool("attach", options.attach) is True: 

1992 assert not display_is_remote 

1993 #maybe the server is already running 

1994 #and we don't need to bother trying to start it: 

1995 try: 

1996 display = pick_display(error_cb, options, args) 

1997 except Exception: 

1998 pass 

1999 else: 

2000 dotxpra = DotXpra(options.socket_dir, options.socket_dirs) 

2001 display_name = display.get("display_name") 

2002 if display_name: 

2003 state = dotxpra.get_display_state(display_name) 

2004 if state==DotXpra.LIVE: 

2005 noerr(sys.stdout.write, "existing live display found, attaching") 

2006 return do_run_mode(script_file, error_cb, options, args, "attach", defaults) 

2007 

2008 if (mode in ("start", "start-desktop", "upgrade", "upgrade-desktop") and supports_server) or \ 

2009 (mode=="shadow" and supports_shadow) or (mode=="proxy" and supports_proxy): 

2010 start_via_proxy = parse_bool("start-via-proxy", options.start_via_proxy) 

2011 if start_via_proxy is not False and (not POSIX or getuid()!=0) and options.daemon: 

2012 try: 

2013 from xpra import client 

2014 assert client 

2015 except ImportError as e: 

2016 if start_via_proxy is True: 

2017 error_cb("cannot start-via-proxy: xpra client is not installed") 

2018 else: 

2019 err = None 

2020 try: 

2021 #this will use the client "start-new-session" feature, 

2022 #to start a new session and connect to it at the same time: 

2023 if not args: 

2024 from xpra.platform.features import SYSTEM_PROXY_SOCKET 

2025 args = [SYSTEM_PROXY_SOCKET] 

2026 app = get_client_app(error_cb, options, args, "request-%s" % mode) 

2027 r = do_run_client(app) 

2028 #OK or got a signal: 

2029 NO_RETRY = [EXIT_OK] + list(range(128, 128+16)) 

2030 if app.completed_startup: 

2031 #if we had connected to the session, 

2032 #we can ignore more error codes: 

2033 NO_RETRY += [ 

2034 EXIT_CONNECTION_LOST, 

2035 EXIT_REMOTE_ERROR, 

2036 EXIT_INTERNAL_ERROR, 

2037 EXIT_FILE_TOO_BIG, 

2038 ] 

2039 if r in NO_RETRY: 

2040 return r 

2041 if r==EXIT_FAILURE: 

2042 err = "unknown general failure" 

2043 else: 

2044 err = EXIT_STR.get(r, r) 

2045 except Exception as e: 

2046 log = Logger("proxy") 

2047 log("failed to start via proxy", exc_info=True) 

2048 err = str(e) 

2049 if start_via_proxy is True: 

2050 raise InitException("failed to start-via-proxy: %s" % (err,)) 

2051 #warn and fall through to regular server start: 

2052 warn("Warning: cannot use the system proxy for '%s' subcommand," % (mode, )) 

2053 warn(" %s" % (err,)) 

2054 warn(" more information may be available in your system log") 

2055 #re-exec itself and disable start-via-proxy: 

2056 args = sys.argv[:]+["--start-via-proxy=no"] 

2057 #warn("re-running with: %s" % (args,)) 

2058 os.execv(args[0], args) 

2059 #this code should be unreachable! 

2060 return 1 

2061 #show splash screen? 

2062 progress_cb = None 

2063 def is_splash_enabled(splash): 

2064 if splash in (True, False): 

2065 return splash 

2066 #auto mode, figure out if we should show it: 

2067 if not POSIX: 

2068 return True 

2069 if options.daemon: 

2070 #daemon mode would have problems with the pipes 

2071 return False 

2072 if os.environ.get("SSH_CONNECTION") or os.environ.get("SSH_CLIENT"): 

2073 #don't show the splash screen over SSH forwarding 

2074 return False 

2075 xdisplay = os.environ.get("DISPLAY") 

2076 if xdisplay: 

2077 #make sure that the display isn't the one we're running against, 

2078 #unless we're shadowing it 

2079 return xdisplay!=display or mode=="shadow" 

2080 if os.environ.get("XDG_SESSION_DESKTOP"): 

2081 return True 

2082 if is_splash_enabled(options.splash): 

2083 # use splash screen to show server startup progress: 

2084 progress = make_progress_process() 

2085 def stop_progress_process(): 

2086 if progress.poll() is not None: 

2087 return 

2088 try: 

2089 progress.terminate() 

2090 except Exception: 

2091 pass 

2092 def show_progress(pct, text=""): 

2093 if progress.poll() is not None: 

2094 return 

2095 noerr(progress.stdin.write, ("%i:%s\n" % (pct, text)).encode("latin1")) 

2096 noerr(progress.stdin.flush) 

2097 if pct==100: 

2098 #it should exit on its own, but just in case: 

2099 from xpra.common import SPLASH_EXIT_DELAY 

2100 from gi.repository import GLib 

2101 GLib.timeout_add(SPLASH_EXIT_DELAY*1000+500, stop_progress_process) 

2102 progress_cb = show_progress 

2103 from xpra.scripts.server import add_cleanup 

2104 add_cleanup(stop_progress_process) 

2105 try: 

2106 cwd = os.getcwd() 

2107 except OSError: 

2108 os.chdir("/") 

2109 cwd = "/" 

2110 env = os.environ.copy() 

2111 desktop_display = nox() 

2112 try: 

2113 from xpra import server 

2114 assert server 

2115 from xpra.scripts.server import do_run_server, add_when_ready, run_cleanups 

2116 except ImportError as e: 

2117 error_cb("Xpra server is not installed") 

2118 ###################################################################### 

2119 if options.attach is True: 

2120 def attach_client(): 

2121 from xpra.platform.paths import get_xpra_command 

2122 cmd = get_xpra_command()+["attach"] 

2123 display_name = os.environ.get("DISPLAY") 

2124 if display_name: 

2125 cmd += [display_name] 

2126 #options has been "fixed up", make sure this has too: 

2127 fixup_options(defaults) 

2128 for x in CLIENT_OPTIONS: 

2129 f = x.replace("-", "_") 

2130 try: 

2131 d = getattr(defaults, f) 

2132 c = getattr(options, f) 

2133 except Exception as e: 

2134 print("error on %s: %s" % (f, e)) 

2135 continue 

2136 if c!=d: 

2137 if OPTION_TYPES.get(x)==list: 

2138 v = csv(c) 

2139 else: 

2140 v = str(c) 

2141 cmd.append("--%s=%s" % (x, v)) 

2142 proc = Popen(cmd, cwd=cwd, env=env, start_new_session=POSIX and not OSX) 

2143 from xpra.child_reaper import getChildReaper 

2144 getChildReaper().add_process(proc, "client-attach", cmd, ignore=True, forget=False) 

2145 add_when_ready(attach_client) 

2146 #add finally hook to ensure we will run the cleanups 

2147 #even if we exit because of an exception: 

2148 try: 

2149 return do_run_server(error_cb, options, mode, script_file, args, desktop_display, progress_cb) 

2150 finally: 

2151 run_cleanups() 

2152 import gc 

2153 gc.collect() 

2154 

2155 

2156def run_remote_server(error_cb, opts, args, mode, defaults): 

2157 """ Uses the regular XpraClient with patched proxy arguments to tell run_proxy to start the server """ 

2158 params = parse_display_name(error_cb, opts, args[0]) 

2159 hello_extra = {} 

2160 #strip defaults, only keep extra ones: 

2161 for x in START_COMMAND_OPTIONS: # ["start", "start-child", etc] 

2162 fn = x.replace("-", "_") 

2163 v = strip_defaults_start_child(getattr(opts, fn), getattr(defaults, fn)) 

2164 setattr(opts, fn, v) 

2165 if isdisplaytype(args, "ssh"): 

2166 #add special flags to "display_as_args" 

2167 proxy_args = [] 

2168 if params.get("display") is not None: 

2169 proxy_args.append(params["display"]) 

2170 for x in get_start_server_args(opts, compat=True): 

2171 proxy_args.append(x) 

2172 #we have consumed the start[-child] options 

2173 opts.start_child = [] 

2174 opts.start = [] 

2175 params["display_as_args"] = proxy_args 

2176 #and use a proxy subcommand to start the server: 

2177 params["proxy_command"] = [{ 

2178 "shadow" : "_proxy_shadow_start", 

2179 "start" : "_proxy_start", 

2180 "start-desktop" : "_proxy_start_desktop", 

2181 }.get(mode)] 

2182 else: 

2183 #tcp, ssl or vsock: 

2184 sns = { 

2185 "mode" : mode, 

2186 "display" : params.get("display", ""), 

2187 } 

2188 for x in START_COMMAND_OPTIONS: 

2189 fn = x.replace("-", "_") 

2190 v = getattr(opts, fn) 

2191 if v: 

2192 sns[x] = v 

2193 hello_extra = {"start-new-session" : sns} 

2194 

2195 app = None 

2196 try: 

2197 if opts.attach is False: 

2198 from xpra.client.gobject_client_base import WaitForDisconnectXpraClient, RequestStartClient 

2199 if isdisplaytype(args, "ssh"): 

2200 #ssh will start the instance we requested, 

2201 #then we just detach and we're done 

2202 app = WaitForDisconnectXpraClient(opts) 

2203 else: 

2204 app = RequestStartClient(opts) 

2205 app.start_new_session = sns 

2206 app.hello_extra = {"connect" : False} 

2207 else: 

2208 app = make_client(error_cb, opts) 

2209 app.show_progress(30, "client configuration") 

2210 app.init(opts) 

2211 app.show_progress(40, "loading user interface") 

2212 app.init_ui(opts) 

2213 app.hello_extra = hello_extra 

2214 def handshake_complete(*_args): 

2215 app.show_progress(100, "connection established") 

2216 app.after_handshake(handshake_complete) 

2217 app.show_progress(60, "starting server") 

2218 conn = connect_or_fail(params, opts) 

2219 app.setup_connection(conn) 

2220 app.show_progress(80, "connecting to server") 

2221 except Exception as e: 

2222 if app: 

2223 app.show_progress(100, "failure: %s" % e) 

2224 raise 

2225 return do_run_client(app) 

2226 

2227 

2228X11_SOCKET_DIR = "/tmp/.X11-unix/" 

2229 

2230def find_X11_displays(max_display_no=None, match_uid=None, match_gid=None): 

2231 displays = [] 

2232 if os.path.exists(X11_SOCKET_DIR) and os.path.isdir(X11_SOCKET_DIR): 

2233 for x in os.listdir(X11_SOCKET_DIR): 

2234 socket_path = os.path.join(X11_SOCKET_DIR, x) 

2235 if not x.startswith("X"): 

2236 warn("path '%s' does not look like an X11 socket" % socket_path) 

2237 continue 

2238 try: 

2239 v = int(x[1:]) 

2240 except ValueError: 

2241 warn("'%s' does not parse as a display number" % x) 

2242 continue 

2243 try: 

2244 #arbitrary: only shadow automatically displays below 10.. 

2245 if max_display_no and v>max_display_no: 

2246 #warn("display no %i too high (max %i)" % (v, max_display_no)) 

2247 continue 

2248 #check that this is a socket 

2249 sstat = os.stat(socket_path) 

2250 if match_uid is not None and sstat.st_uid!=match_uid: 

2251 #print("display socket %s does not match uid %i (uid=%i)" % (socket_path, match_uid, sstat.st_uid)) 

2252 continue 

2253 if match_gid is not None and sstat.st_gid!=match_gid: 

2254 #print("display socket %s does not match gid %i (gid=%i)" % (socket_path, match_gid, sstat.st_gid)) 

2255 continue 

2256 is_socket = stat.S_ISSOCK(sstat.st_mode) 

2257 if not is_socket: 

2258 warn("display path '%s' is not a socket!" % socket_path) 

2259 continue 

2260 try: 

2261 if VERIFY_X11_SOCKET_TIMEOUT: 

2262 sockpath = os.path.join(X11_SOCKET_DIR, "X%i" % v) 

2263 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 

2264 sock.settimeout(VERIFY_X11_SOCKET_TIMEOUT) 

2265 sock.connect(sockpath) 

2266 except OSError: 

2267 pass 

2268 else: 

2269 #print("found display path '%s'" % socket_path) 

2270 displays.append(v) 

2271 except Exception as e: 

2272 warn("failure on %s: %s" % (socket_path, e)) 

2273 return displays 

2274 

2275def guess_X11_display(dotxpra, current_display, uid=getuid(), gid=getgid()): 

2276 displays = [":%s" % x for x in find_X11_displays(max_display_no=10, match_uid=uid, match_gid=gid)] 

2277 if current_display and current_display not in displays: 

2278 displays.append(current_display) 

2279 if len(displays)!=1: 

2280 #try without uid match: 

2281 displays = [":%s" % x for x in find_X11_displays(max_display_no=10, match_gid=gid)] 

2282 if len(displays)!=1: 

2283 #try without gid match: 

2284 displays = [":%s" % x for x in find_X11_displays(max_display_no=10)] 

2285 if not displays: 

2286 raise InitExit(1, "could not detect any live X11 displays") 

2287 if len(displays)>1: 

2288 #since we are here to shadow, 

2289 #assume we want to shadow a real X11 server, 

2290 #so remove xpra's own displays to narrow things down: 

2291 results = dotxpra.sockets() 

2292 xpra_displays = [display for _, display in results] 

2293 displays = list(set(displays)-set(xpra_displays)) 

2294 if not displays: 

2295 raise InitExit(1, "could not detect any live plain X11 displays,\n" 

2296 +" only multiple xpra displays: %s" % csv(xpra_displays)) 

2297 if current_display: 

2298 return current_display 

2299 if len(displays)!=1: 

2300 raise InitExit(1, "too many live X11 displays to choose from: %s" % csv(sorted(displays))) 

2301 return displays[0] 

2302 

2303 

2304def no_gtk(): 

2305 if OSX: 

2306 #we can't verify on macos because importing GtkosxApplication 

2307 #will import Gtk, and we need GtkosxApplication early to find the paths 

2308 return 

2309 Gtk = sys.modules.get("gi.repository.Gtk") 

2310 if Gtk is None: 

2311 #all good, not loaded 

2312 return 

2313 raise Exception("the Gtk module is already loaded: %s" % Gtk) 

2314 

2315 

2316def run_qrcode(args): 

2317 from xpra.client.gtk3 import qrcode_client 

2318 return qrcode_client.main(args) 

2319 

2320def run_splash(args) -> int: 

2321 from xpra.client.gtk3 import splash_screen 

2322 return splash_screen.main(args) 

2323 

2324def run_glprobe(opts, show=False) -> int: 

2325 if show: 

2326 from xpra.platform.gui import init, set_default_icon 

2327 set_default_icon("opengl.png") 

2328 init() 

2329 props = do_run_glcheck(opts, show) 

2330 if not props.get("success", False): 

2331 return 3 

2332 if not props.get("safe", False): 

2333 return 2 

2334 return 0 

2335 

2336def do_run_glcheck(opts, show=False) -> dict: 

2337 #suspend all logging: 

2338 saved_level = None 

2339 log = Logger("opengl") 

2340 log("do_run_glcheck(.., %s)" % show) 

2341 if not is_debug_enabled("opengl") or not use_tty(): 

2342 saved_level = logging.root.getEffectiveLevel() 

2343 logging.root.setLevel(logging.WARN) 

2344 try: 

2345 from xpra.client.gl.window_backend import ( 

2346 get_gl_client_window_module, 

2347 test_gl_client_window, 

2348 ) 

2349 opengl_str = (opts.opengl or "").lower() 

2350 force_enable = opengl_str.split(":")[0] in TRUE_OPTIONS 

2351 opengl_props, gl_client_window_module = get_gl_client_window_module(force_enable) 

2352 log("do_run_glcheck() opengl_props=%s, gl_client_window_module=%s", opengl_props, gl_client_window_module) 

2353 if gl_client_window_module and (opengl_props.get("safe", False) or force_enable): 

2354 gl_client_window_class = gl_client_window_module.GLClientWindow 

2355 pixel_depth = int(opts.pixel_depth) 

2356 log("do_run_glcheck() gl_client_window_class=%s, pixel_depth=%s", gl_client_window_class, pixel_depth) 

2357 if pixel_depth not in (0, 16, 24, 30) and pixel_depth<32: 

2358 pixel_depth = 0 

2359 draw_result = test_gl_client_window(gl_client_window_class, pixel_depth=pixel_depth, show=show) 

2360 opengl_props.update(draw_result) 

2361 if not draw_result.get("success", False): 

2362 opengl_props["safe"] = False 

2363 log("do_run_glcheck(.., %s)=%s", show, opengl_props) 

2364 return opengl_props 

2365 except Exception as e: 

2366 if is_debug_enabled("opengl"): 

2367 log("do_run_glcheck(..)", exc_info=True) 

2368 if use_tty(): 

2369 noerr(sys.stderr.write, "error=%s\n" % nonl(e)) 

2370 noerr(sys.stderr.flush) 

2371 return { 

2372 "success" : False, 

2373 "message" : str(e).replace("\n", " "), 

2374 } 

2375 finally: 

2376 if saved_level is not None: 

2377 logging.root.setLevel(saved_level) 

2378 

2379def run_glcheck(opts): 

2380 try: 

2381 props = do_run_glcheck(opts) 

2382 except Exception as e: 

2383 noerr(sys.stdout.write, "error=%s\n" % str(e).replace("\n", " ")) 

2384 return 1 

2385 log = Logger("opengl") 

2386 log("run_glcheck(..) props=%s", props) 

2387 for k in sorted(props.keys()): 

2388 v = props[k] 

2389 #skip not human readable: 

2390 if k not in ("extensions", "glconfig", "GLU.extensions", ): 

2391 vstr = str(v) 

2392 try: 

2393 if k.endswith("dims"): 

2394 vstr = csv(v) 

2395 else: 

2396 vstr = pver(v) 

2397 except ValueError: 

2398 pass 

2399 sys.stdout.write("%s=%s\n" % (k, vstr)) 

2400 sys.stdout.flush() 

2401 return 0 

2402 

2403 

2404def pick_shadow_display(dotxpra, args, uid=getuid(), gid=getgid()): 

2405 if OSX or WIN32: 

2406 #no need for a specific display 

2407 return "Main" 

2408 if len(args)==1 and args[0] and args[0][0]==":": 

2409 #display_name was provided: 

2410 return args[0] 

2411 return guess_X11_display(dotxpra, None, uid, gid) 

2412 

2413 

2414def start_macos_shadow(cmd, env, cwd): 

2415 #launch the shadow server via launchctl so it will have GUI access: 

2416 LAUNCH_AGENT = "org.xpra.Agent" 

2417 LAUNCH_AGENT_FILE = "/System/Library/LaunchAgents/%s.plist" % LAUNCH_AGENT 

2418 try: 

2419 os.stat(LAUNCH_AGENT_FILE) 

2420 except Exception as e: 

2421 #ignore access denied error, launchctl runs as root 

2422 import errno 

2423 if e.args[0]!=errno.EACCES: 

2424 warn("Error: shadow may not start,\n" 

2425 +" the launch agent file '%s' seems to be missing:%s.\n" % (LAUNCH_AGENT_FILE, e)) 

2426 argfile = os.path.expanduser("~/.xpra/shadow-args") 

2427 with open(argfile, "w") as f: 

2428 f.write('["Xpra", "--no-daemon"') 

2429 for x in cmd[1:]: 

2430 f.write(', "%s"' % x) 

2431 f.write(']') 

2432 launch_commands = [ 

2433 ["launchctl", "unload", LAUNCH_AGENT_FILE], 

2434 ["launchctl", "load", "-S", "Aqua", LAUNCH_AGENT_FILE], 

2435 ["launchctl", "start", LAUNCH_AGENT], 

2436 ] 

2437 log = get_util_logger() 

2438 log("start_server_subprocess: launch_commands=%s", launch_commands) 

2439 for x in launch_commands: 

2440 proc = Popen(x, env=env, cwd=cwd) 

2441 proc.wait() 

2442 proc = None 

2443 

2444def proxy_start_win32_shadow(script_file, args, opts, dotxpra, display_name): 

2445 log = Logger("server") 

2446 from xpra.platform.paths import get_app_dir 

2447 app_dir = get_app_dir() 

2448 cwd = app_dir 

2449 env = os.environ.copy() 

2450 exe = script_file 

2451 cmd = [] 

2452 if envbool("XPRA_PAEXEC", True): 

2453 #use paexec to access the GUI session: 

2454 paexec = os.path.join(app_dir, "paexec.exe") 

2455 if os.path.exists(paexec) and os.path.isfile(paexec): 

2456 from xpra.platform.win32.wtsapi import find_session 

2457 from xpra.platform import get_username 

2458 username = get_username() 

2459 info = find_session(username) 

2460 if info: 

2461 cmd = [ 

2462 "paexec.exe", 

2463 "-i", str(info["SessionID"]), "-s", 

2464 ] 

2465 exe = paexec 

2466 #don't show a cmd window: 

2467 script_file = script_file.replace("Xpra_cmd.exe", "Xpra.exe") 

2468 else: 

2469 log("session not found for user '%s', not using paexec", username) 

2470 cmd += [script_file, "shadow"] + args 

2471 cmd += get_start_server_args(opts) 

2472 debug_args = os.environ.get("XPRA_SHADOW_DEBUG") 

2473 if debug_args is None: 

2474 debug_args = ",".join(get_debug_args()) 

2475 if debug_args: 

2476 cmd.append("--debug=%s" % debug_args) 

2477 log("proxy shadow start command: %s", cmd) 

2478 proc = Popen(cmd, executable=exe, env=env, cwd=cwd) 

2479 start = monotonic_time() 

2480 elapsed = 0 

2481 while elapsed<WAIT_SERVER_TIMEOUT: 

2482 state = dotxpra.get_display_state(display_name) 

2483 if state==DotXpra.LIVE: 

2484 log("found live server '%s'", display_name) 

2485 #give it a bit of time: 

2486 #FIXME: poll until the server is ready instead 

2487 sleep(1) 

2488 return proc, "named-pipe://%s" % display_name, display_name 

2489 log("get_display_state(%s)=%s (waiting)", display_name, state) 

2490 if proc.poll() not in (None, 0): 

2491 raise Exception("shadow subprocess command returned %s", proc.returncode) 

2492 sleep(0.10) 

2493 elapsed = monotonic_time()-start 

2494 proc.terminate() 

2495 raise Exception("timeout: failed to identify the new shadow server '%s'" % display_name) 

2496 

2497def start_server_subprocess(script_file, args, mode, opts, username="", uid=getuid(), gid=getgid(), env=os.environ.copy(), cwd=None): 

2498 log = Logger("server", "exec") 

2499 log("start_server_subprocess%s", (script_file, args, mode, opts, uid, gid, env, cwd)) 

2500 dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs, username, uid=uid, gid=gid) 

2501 #we must use a subprocess to avoid messing things up - yuk 

2502 assert mode in ("start", "start-desktop", "shadow") 

2503 if mode in ("start", "start-desktop"): 

2504 if len(args)==1: 

2505 display_name = args[0] 

2506 elif len(args)==0: 

2507 #let the server get one from Xorg via displayfd: 

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

2509 else: 

2510 raise InitException("%s: expected 0 or 1 arguments but got %s: %s" % (mode, len(args), args)) 

2511 else: 

2512 assert mode=="shadow" 

2513 assert len(args) in (0, 1), "starting shadow server: expected 0 or 1 arguments but got %s: %s" % (len(args), args) 

2514 display_name = pick_shadow_display(dotxpra, args, uid, gid) 

2515 #we now know the display name, so add it: 

2516 args = [display_name] 

2517 opts.exit_with_client = True 

2518 

2519 #get the list of existing sockets so we can spot the new ones: 

2520 if display_name.startswith("S"): 

2521 matching_display = None 

2522 else: 

2523 matching_display = display_name 

2524 if WIN32: 

2525 assert mode=="shadow" 

2526 assert display_name 

2527 return proxy_start_win32_shadow(script_file, args, opts, dotxpra, display_name) 

2528 

2529 existing_sockets = set(dotxpra.socket_paths(check_uid=uid, 

2530 matching_state=dotxpra.LIVE, 

2531 matching_display=matching_display)) 

2532 log("start_server_subprocess: existing_sockets=%s", existing_sockets) 

2533 

2534 cmd = [script_file, mode] + args #ie: ["/usr/bin/xpra", "start-desktop", ":100"] 

2535 cmd += get_start_server_args(opts, uid, gid) #ie: ["--exit-with-children", "--start-child=xterm"] 

2536 debug_args = os.environ.get("XPRA_SUBPROCESS_DEBUG") 

2537 if debug_args is None: 

2538 debug_args = ",".join(get_debug_args()) 

2539 if debug_args: 

2540 cmd.append("--debug=%s" % debug_args) 

2541 #when starting via the system proxy server, 

2542 #we may already have a XPRA_PROXY_START_UUID, 

2543 #specified by the proxy-start command: 

2544 new_server_uuid = parse_env(opts.env or []).get("XPRA_PROXY_START_UUID") 

2545 if not new_server_uuid: 

2546 #generate one now: 

2547 from xpra.os_util import get_hex_uuid 

2548 new_server_uuid = get_hex_uuid() 

2549 cmd.append("--env=XPRA_PROXY_START_UUID=%s" % new_server_uuid) 

2550 if mode=="shadow" and OSX: 

2551 start_macos_shadow(cmd, env, cwd) 

2552 proc = None 

2553 else: 

2554 #useful for testing failures that cause the whole XDG_RUNTIME_DIR to get nuked 

2555 #(and the log file with it): 

2556 #cmd.append("--log-file=/tmp/proxy.log") 

2557 preexec_fn = None 

2558 pass_fds = () 

2559 if POSIX: 

2560 preexec_fn = os.setpgrp 

2561 cmd.append("--daemon=yes") 

2562 cmd.append("--systemd-run=no") 

2563 if getuid()==0 and (uid!=0 or gid!=0): 

2564 cmd.append("--uid=%i" % uid) 

2565 cmd.append("--gid=%i" % gid) 

2566 if not OSX and not matching_display: 

2567 #use "--displayfd" switch to tell us which display was chosen: 

2568 r_pipe, w_pipe = os.pipe() 

2569 log("subprocess displayfd pipes: %s", (r_pipe, w_pipe)) 

2570 cmd.append("--displayfd=%s" % w_pipe) 

2571 pass_fds = (w_pipe, ) 

2572 log("start_server_subprocess: command=%s", csv(["'%s'" % x for x in cmd])) 

2573 proc = Popen(cmd, env=env, cwd=cwd, preexec_fn=preexec_fn, pass_fds=pass_fds) 

2574 log("proc=%s", proc) 

2575 if POSIX and not OSX and not matching_display: 

2576 from xpra.platform.displayfd import read_displayfd, parse_displayfd 

2577 buf = read_displayfd(r_pipe, proc=None) #proc deamonizes! 

2578 noerr(os.close, r_pipe) 

2579 noerr(os.close, w_pipe) 

2580 def displayfd_err(msg): 

2581 log.error("Error: displayfd failed") 

2582 log.error(" %s", msg) 

2583 n = parse_displayfd(buf, displayfd_err) 

2584 if n is not None: 

2585 matching_display = ":%s" % n 

2586 log("displayfd=%s", matching_display) 

2587 socket_path, display = identify_new_socket(proc, dotxpra, existing_sockets, matching_display, new_server_uuid, display_name, uid) 

2588 return proc, socket_path, display 

2589 

2590def get_start_server_args(opts, uid=getuid(), gid=getgid(), compat=False): 

2591 defaults = make_defaults_struct(uid=uid, gid=gid) 

2592 fdefaults = defaults.clone() 

2593 fixup_options(fdefaults) 

2594 args = [] 

2595 for x, ftype in OPTION_TYPES.items(): 

2596 if x in NON_COMMAND_LINE_OPTIONS or x in CLIENT_ONLY_OPTIONS: 

2597 continue 

2598 if compat and x in OPTIONS_ADDED_SINCE_V3: 

2599 continue 

2600 fn = x.replace("-", "_") 

2601 ov = getattr(opts, fn) 

2602 dv = getattr(defaults, fn) 

2603 fv = getattr(fdefaults, fn) 

2604 if ftype==list: 

2605 #compare lists using their csv representation: 

2606 if csv(ov)==csv(dv) or csv(ov)==csv(fv): 

2607 continue 

2608 if ov in (dv, fv): 

2609 continue #same as the default 

2610 argname = "--%s=" % x 

2611 if compat: 

2612 argname = OPTIONS_COMPAT_NAMES.get(argname, argname) 

2613 #lists are special cased depending on how OptionParse will be parsing them: 

2614 if ftype==list: 

2615 #warn("%s: %s vs %s\n" % (x, ov, dv)) 

2616 if x in START_COMMAND_OPTIONS+BIND_OPTIONS+[ 

2617 "pulseaudio-configure-commands", 

2618 "speaker-codec", "microphone-codec", 

2619 "key-shortcut", "start-env", "env", 

2620 "socket-dirs", 

2621 ]: 

2622 #individual arguments (ie: "--start=xterm" "--start=gedit" ..) 

2623 for e in ov: 

2624 args.append("%s%s" % (argname, e)) 

2625 else: 

2626 #those can be specified as CSV: (ie: "--encodings=png,jpeg,rgb") 

2627 args.append("%s%s" % (argname, ",".join(str(v) for v in ov))) 

2628 elif ftype==bool: 

2629 if compat and x in ("exit-with-children", "mmap-group"): 

2630 #older servers don't take a bool value for those options, 

2631 #it is disabled unless specified: 

2632 if ov: 

2633 args.append("--%s" % x) 

2634 else: 

2635 args.append("%s%s" % (argname, ["no", "yes"][int(ov)])) 

2636 elif ftype in (int, float, str): 

2637 args.append("%s%s" % (argname, ov)) 

2638 else: 

2639 raise InitException("unknown option type '%s' for '%s'" % (ftype, x)) 

2640 return args 

2641 

2642 

2643def identify_new_socket(proc, dotxpra, existing_sockets, matching_display, new_server_uuid, display_name, matching_uid=0): 

2644 log = Logger("server", "network") 

2645 log("identify_new_socket%s", 

2646 (proc, dotxpra, existing_sockets, matching_display, new_server_uuid, display_name, matching_uid)) 

2647 #wait until the new socket appears: 

2648 start = monotonic_time() 

2649 UUID_PREFIX = "uuid=" 

2650 DISPLAY_PREFIX = "display=" 

2651 from xpra.platform.paths import get_nodock_command 

2652 while monotonic_time()-start<WAIT_SERVER_TIMEOUT and (proc is None or proc.poll() in (None, 0)): 

2653 sockets = set(dotxpra.socket_paths(check_uid=matching_uid, matching_state=dotxpra.LIVE, matching_display=matching_display)) 

2654 #sort because we prefer a socket in /run/* to one in /home/*: 

2655 new_sockets = tuple(reversed(tuple(sockets-existing_sockets))) 

2656 log("identify_new_socket new_sockets=%s", new_sockets) 

2657 for socket_path in new_sockets: 

2658 #verify that this is the right server: 

2659 try: 

2660 #we must use a subprocess to avoid messing things up - yuk 

2661 cmd = get_nodock_command()+["id", "socket:%s" % socket_path] 

2662 p = Popen(cmd, stdout=PIPE, stderr=PIPE) 

2663 stdout, _ = p.communicate() 

2664 if p.returncode==0: 

2665 try: 

2666 out = stdout.decode('utf-8') 

2667 except: 

2668 try: 

2669 out = stdout.decode() 

2670 except: 

2671 out = bytestostr(stdout) 

2672 lines = out.splitlines() 

2673 log("id(%s): %s", socket_path, csv(lines)) 

2674 found = False 

2675 display = matching_display 

2676 for line in lines: 

2677 if line.startswith(UUID_PREFIX): 

2678 this_uuid = line[len(UUID_PREFIX):] 

2679 if this_uuid==new_server_uuid: 

2680 found = True 

2681 elif line.startswith(DISPLAY_PREFIX): 

2682 display = line[len(DISPLAY_PREFIX):] 

2683 if display and display==matching_display: 

2684 found = True 

2685 if found: 

2686 assert display, "display value not found in id output" 

2687 log("identify_new_socket found match: path=%s, display=%s", socket_path, display) 

2688 return socket_path, display 

2689 except Exception as e: 

2690 warn("error during server process detection: %s" % e) 

2691 sleep(0.10) 

2692 raise InitException("failed to identify the new server display!") 

2693 

2694def run_proxy(error_cb, opts, script_file, args, mode, defaults): 

2695 no_gtk() 

2696 display = None 

2697 if mode in ("_proxy_start", "_proxy_start_desktop", "_proxy_shadow_start"): 

2698 attach = parse_bool("attach", opts.attach) 

2699 state = None 

2700 if attach is not False: 

2701 #maybe this server already exists? 

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

2703 display_name = None 

2704 if not args and mode=="_proxy_shadow_start": 

2705 try: 

2706 display_name = pick_shadow_display(dotxpra, args) 

2707 except Exception: 

2708 #failed to guess! 

2709 pass 

2710 else: 

2711 args = [display_name] 

2712 elif args: 

2713 display = pick_display(error_cb, opts, args) 

2714 display_name = display.get("display_name") 

2715 if display_name: 

2716 state = dotxpra.get_display_state(display_name) 

2717 if state!=DotXpra.DEAD: 

2718 sys.stderr.write("found existing display %s : %s\n" % (display_name, state)) 

2719 if state!=DotXpra.LIVE: 

2720 server_mode = { 

2721 "_proxy_start" : "start", 

2722 "_proxy_start_desktop" : "start-desktop", 

2723 "_proxy_shadow_start" : "shadow", 

2724 }[mode] 

2725 #strip defaults, only keep extra ones: 

2726 for x in ("start", "start-child", 

2727 "start-after-connect", "start-child-after-connect", 

2728 "start-on-connect", "start-child-on-connect", 

2729 "start-on-last-client-exit", "start-child-on-last-client-exit", 

2730 ): 

2731 fn = x.replace("-", "_") 

2732 v = strip_defaults_start_child(getattr(opts, fn), getattr(defaults, fn)) 

2733 setattr(opts, fn, v) 

2734 opts.splash = False 

2735 proc, socket_path, display = start_server_subprocess(script_file, args, server_mode, opts) 

2736 if not socket_path: 

2737 #if we return non-zero, we will try the next run-xpra script in the list.. 

2738 return 0 

2739 if WIN32: 

2740 uri = "named-pipe://%s" % display_name 

2741 else: 

2742 uri = "socket://%s" % socket_path 

2743 display = parse_display_name(error_cb, opts, uri) 

2744 if proc and proc.poll() is None: 

2745 #start a thread just to reap server startup process (yuk) 

2746 #(as the server process will exit as it daemonizes) 

2747 from xpra.make_thread import start_thread 

2748 start_thread(proc.wait, "server-startup-reaper") 

2749 if not display: 

2750 #use display specified on command line: 

2751 display = pick_display(error_cb, opts, args) 

2752 server_conn = connect_or_fail(display, opts) 

2753 from xpra.scripts.fdproxy import XpraProxy 

2754 from xpra.net.bytestreams import TwoFileConnection 

2755 pipe = TwoFileConnection(sys.stdout, sys.stdin, socktype="stdin/stdout") 

2756 app = XpraProxy("xpra-pipe-proxy", pipe, server_conn) 

2757 app.run() 

2758 return 0 

2759 

2760def run_stopexit(mode, error_cb, opts, extra_args): 

2761 assert mode in ("stop", "exit") 

2762 no_gtk() 

2763 

2764 def show_final_state(display_desc): 

2765 #this is for local sockets only! 

2766 display = display_desc["display"] 

2767 sockdir = display_desc.get("socket_dir", "") 

2768 sockdirs = display_desc.get("socket_dirs", []) 

2769 sockdir = DotXpra(sockdir, sockdirs) 

2770 try: 

2771 sockfile = get_sockpath(display_desc, error_cb, 0) 

2772 except InitException: 

2773 #on win32, we can't find the path when it is gone 

2774 final_state = DotXpra.DEAD 

2775 else: 

2776 #first 5 seconds: just check if the socket still exists: 

2777 #without connecting (avoid warnings and log messages on server) 

2778 for _ in range(25): 

2779 if not os.path.exists(sockfile): 

2780 break 

2781 sleep(0.2) 

2782 #next 5 seconds: actually try to connect 

2783 for _ in range(5): 

2784 final_state = sockdir.get_server_state(sockfile, 1) 

2785 if final_state is DotXpra.DEAD: 

2786 break 

2787 sleep(1) 

2788 if final_state is DotXpra.DEAD: 

2789 print("xpra at %s has exited." % display) 

2790 return 0 

2791 if final_state is DotXpra.UNKNOWN: 

2792 print("How odd... I'm not sure what's going on with xpra at %s" % display) 

2793 return 1 

2794 if final_state is DotXpra.LIVE: 

2795 print("Failed to shutdown xpra at %s" % display) 

2796 return 1 

2797 raise Exception("invalid state: %s" % final_state) 

2798 

2799 def multimode(displays): 

2800 sys.stdout.write("Trying to %s %i displays:\n" % (mode, len(displays))) 

2801 sys.stdout.write(" %s\n" % csv(displays)) 

2802 procs = [] 

2803 #["xpra", "stop", ..] 

2804 from xpra.platform.paths import get_nodock_command 

2805 cmd = get_nodock_command()+[mode, "--socket-dir=%s" % opts.socket_dir] 

2806 for x in opts.socket_dirs: 

2807 if x: 

2808 cmd.append("--socket-dirs=%s" % x) 

2809 #use a subprocess per display: 

2810 for display in displays: 

2811 dcmd = cmd + [display] 

2812 proc = Popen(dcmd) 

2813 procs.append(proc) 

2814 start = monotonic_time() 

2815 live = procs 

2816 while monotonic_time()-start<10 and live: 

2817 live = [x for x in procs if x.poll() is None] 

2818 return 0 

2819 

2820 if len(extra_args)==1 and extra_args[0]=="all": 

2821 #stop or exit all 

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

2823 displays = dotxpra.displays(check_uid=getuid(), matching_state=DotXpra.LIVE) 

2824 if not displays: 

2825 sys.stdout.write("No xpra sessions found\n") 

2826 return 1 

2827 if len(displays)==1: 

2828 #fall through, but use the display we found: 

2829 extra_args = displays 

2830 else: 

2831 assert len(displays)>1 

2832 return multimode(displays) 

2833 elif len(extra_args)>1: 

2834 return multimode(extra_args) 

2835 

2836 display_desc = pick_display(error_cb, opts, extra_args) 

2837 app = None 

2838 e = 1 

2839 try: 

2840 if mode=="stop": 

2841 from xpra.client.gobject_client_base import StopXpraClient 

2842 app = StopXpraClient(opts) 

2843 else: 

2844 assert mode=="exit" 

2845 from xpra.client.gobject_client_base import ExitXpraClient 

2846 app = ExitXpraClient(opts) 

2847 app.display_desc = display_desc 

2848 connect_to_server(app, display_desc, opts) 

2849 e = app.run() 

2850 finally: 

2851 if app: 

2852 app.cleanup() 

2853 if e==0: 

2854 if display_desc["local"] and display_desc.get("display"): 

2855 show_final_state(display_desc) 

2856 else: 

2857 print("Sent shutdown command") 

2858 return e 

2859 

2860 

2861def may_cleanup_socket(state, display, sockpath, clean_states=(DotXpra.DEAD,)): 

2862 sys.stdout.write("\t%s session at %s" % (state, display)) 

2863 if state in clean_states: 

2864 try: 

2865 stat_info = os.stat(sockpath) 

2866 if stat_info.st_uid==getuid(): 

2867 os.unlink(sockpath) 

2868 sys.stdout.write(" (cleaned up)") 

2869 except OSError as e: 

2870 sys.stdout.write(" (delete failed: %s)" % e) 

2871 sys.stdout.write("\n") 

2872 

2873 

2874def run_top(error_cb, options, args): 

2875 from xpra.client.top_client import TopClient, TopSessionClient 

2876 app = None 

2877 if args: 

2878 try: 

2879 display_desc = pick_display(error_cb, options, args) 

2880 except Exception: 

2881 pass 

2882 else: 

2883 #show the display we picked automatically: 

2884 app = TopSessionClient(options) 

2885 try: 

2886 connect_to_server(app, display_desc, options) 

2887 except Exception: 

2888 app = None 

2889 if not app: 

2890 #show all sessions: 

2891 app = TopClient(options) 

2892 return app.run() 

2893 

2894 

2895def run_sessions_gui(error_cb, options): 

2896 mdns = supports_mdns and options.mdns 

2897 if mdns: 

2898 return run_mdns_gui(error_cb, options) 

2899 from xpra.client.gtk_base import sessions_gui 

2900 return sessions_gui.do_main(options) 

2901 

2902def run_mdns_gui(error_cb, options): 

2903 from xpra.net.mdns import get_listener_class 

2904 listener = get_listener_class() 

2905 if not listener: 

2906 error_cb("sorry, 'mdns-gui' is not supported on this platform yet") 

2907 from xpra.client.gtk_base import mdns_gui 

2908 return mdns_gui.do_main(options) 

2909 

2910def run_list_mdns(error_cb, extra_args): 

2911 no_gtk() 

2912 if len(extra_args)<=1: 

2913 try: 

2914 MDNS_WAIT = int(extra_args[0]) 

2915 except (IndexError, ValueError): 

2916 MDNS_WAIT = 5 

2917 else: 

2918 error_cb("too many arguments for mode") 

2919 assert supports_mdns 

2920 from xpra.net.mdns import XPRA_MDNS_TYPE 

2921 try: 

2922 from xpra.net.mdns.avahi_listener import AvahiListener 

2923 except ImportError: 

2924 error_cb("sorry, 'list-mdns' is not supported on this platform yet") 

2925 from xpra.net.net_util import if_indextoname 

2926 from xpra.dbus.common import loop_init 

2927 from gi.repository import GLib 

2928 loop_init() 

2929 found = {} 

2930 shown = set() 

2931 def show_new_found(): 

2932 new_found = [x for x in found.keys() if x not in shown] 

2933 for uq in new_found: 

2934 recs = found[uq] 

2935 for i, rec in enumerate(recs): 

2936 iface, _, _, host, address, port, text = rec 

2937 uuid = text.get("uuid") 

2938 display = text.get("display", "") 

2939 mode = text.get("mode", "") 

2940 username = text.get("username", "") 

2941 session = text.get("session") 

2942 dtype = text.get("type") 

2943 if i==0: 

2944 print("* user '%s' on '%s'" % (username, host)) 

2945 if session: 

2946 print(" %s session '%s', uuid=%s" % (dtype, session, uuid)) 

2947 elif uuid: 

2948 print(" uuid=%s" % uuid) 

2949 print(" + %s endpoint on host %s, port %i, interface %s" % (mode, address, port, iface)) 

2950 dstr = "" 

2951 if display.startswith(":"): 

2952 dstr = display[1:] 

2953 uri = "%s/%s@%s:%s/%s" % (mode, username, address, port, dstr) 

2954 print(" \"%s\"" % uri) 

2955 shown.add(uq) 

2956 def mdns_add(interface, _protocol, name, _stype, domain, host, address, port, text): 

2957 text = text or {} 

2958 iface = interface 

2959 if if_indextoname: 

2960 iface = if_indextoname(interface) 

2961 username = text.get("username", "") 

2962 uq = text.get("uuid", len(found)), username, host 

2963 found.setdefault(uq, []).append((iface, name, domain, host, address, port, text)) 

2964 GLib.timeout_add(1000, show_new_found) 

2965 listener = AvahiListener(XPRA_MDNS_TYPE, mdns_add=mdns_add) 

2966 print("Looking for xpra services via mdns") 

2967 try: 

2968 GLib.idle_add(listener.start) 

2969 loop = GLib.MainLoop() 

2970 GLib.timeout_add(MDNS_WAIT*1000, loop.quit) 

2971 loop.run() 

2972 finally: 

2973 listener.stop() 

2974 if not found: 

2975 print("no services found") 

2976 else: 

2977 from xpra.util import engs 

2978 print("%i service%s found" % (len(found), engs(found))) 

2979 

2980 

2981def run_list(error_cb, opts, extra_args): 

2982 no_gtk() 

2983 if extra_args: 

2984 error_cb("too many arguments for mode") 

2985 dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs+opts.client_socket_dirs) 

2986 results = dotxpra.socket_details() 

2987 if not results: 

2988 sys.stdout.write("No xpra sessions found\n") 

2989 return 0 

2990 sys.stdout.write("Found the following xpra sessions:\n") 

2991 unknown = [] 

2992 for socket_dir, values in results.items(): 

2993 sys.stdout.write("%s:\n" % socket_dir) 

2994 for state, display, sockpath in values: 

2995 may_cleanup_socket(state, display, sockpath) 

2996 if state is DotXpra.UNKNOWN: 

2997 unknown.append((socket_dir, display, sockpath)) 

2998 #now, re-probe the "unknown" ones: 

2999 #but only re-probe the ones we own: 

3000 reprobe = [] 

3001 for x in unknown: 

3002 try: 

3003 stat_info = os.stat(x[2]) 

3004 if stat_info.st_uid==getuid(): 

3005 reprobe.append(x) 

3006 except OSError: 

3007 pass 

3008 if reprobe: 

3009 sys.stdout.write("Re-probing unknown sessions in: %s\n" % csv(list(set(x[0] for x in unknown)))) 

3010 counter = 0 

3011 timeout = LIST_REPROBE_TIMEOUT 

3012 while reprobe and counter<timeout: 

3013 sleep(1) 

3014 counter += 1 

3015 probe_list = list(reprobe) 

3016 unknown = [] 

3017 for v in probe_list: 

3018 socket_dir, display, sockpath = v 

3019 state = dotxpra.get_server_state(sockpath, 1) 

3020 if state is DotXpra.DEAD: 

3021 may_cleanup_socket(state, display, sockpath) 

3022 elif state is DotXpra.UNKNOWN: 

3023 unknown.append(v) 

3024 else: 

3025 sys.stdout.write("\t%s session at %s (%s)\n" % (state, display, socket_dir)) 

3026 reprobe = unknown 

3027 if reprobe and timeout==LIST_REPROBE_TIMEOUT: 

3028 #if all the remaining sockets are old, 

3029 #we don't need to poll for very long, 

3030 #as they're very likely to be dead: 

3031 newest = 0 

3032 import time 

3033 for x in reprobe: 

3034 sockpath = x[2] 

3035 try: 

3036 mtime = os.stat(sockpath).st_mtime 

3037 except Exception: 

3038 pass 

3039 else: 

3040 newest = max(mtime, newest) 

3041 elapsed = time.time()-newest 

3042 if elapsed>60*5: 

3043 #wait maximum 3 seconds for old sockets 

3044 timeout = min(LIST_REPROBE_TIMEOUT, 3) 

3045 #now cleanup those still unknown: 

3046 clean_states = [DotXpra.DEAD, DotXpra.UNKNOWN] 

3047 for state, display, sockpath in unknown: 

3048 state = dotxpra.get_server_state(sockpath) 

3049 may_cleanup_socket(state, display, sockpath, clean_states=clean_states) 

3050 return 0 

3051 

3052 

3053def run_list_windows(error_cb, opts, extra_args): 

3054 no_gtk() 

3055 if extra_args: 

3056 error_cb("too many arguments for mode") 

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

3058 displays = dotxpra.displays() 

3059 if not displays: 

3060 sys.stdout.write("No xpra sessions found\n") 

3061 return 0 

3062 import re 

3063 def sort_human(l): 

3064 convert = lambda text: float(text) if text.isdigit() else text 

3065 alphanum = lambda key: [convert(c) for c in re.split('([-+]?[0-9]*\.?[0-9]*)', key)] 

3066 l.sort(key=alphanum) 

3067 return l 

3068 def exec_and_parse(subcommand="id", display=""): 

3069 from xpra.platform.paths import get_nodock_command 

3070 cmd = get_nodock_command()+[subcommand, display] 

3071 d = {} 

3072 try: 

3073 env = os.environ.copy() 

3074 env["XPRA_SKIP_UI"] = "1" 

3075 proc = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env) 

3076 out, err = proc.communicate() 

3077 for line in bytestostr(out or err).splitlines(): 

3078 try: 

3079 k,v = line.split("=", 1) 

3080 d[k] = v 

3081 except ValueError: 

3082 continue 

3083 except Exception: 

3084 pass 

3085 return d 

3086 

3087 sys.stdout.write("Display Status Name Windows\n") 

3088 for display in sort_human(displays): 

3089 state = dotxpra.get_display_state(display) 

3090 sys.stdout.write("%-10s%-10s" % (display, state)) 

3091 sys.stdout.flush() 

3092 name = "?" 

3093 if state==DotXpra.LIVE: 

3094 name = exec_and_parse("id", display).get("session-name", "?") 

3095 if len(name)>=15: 

3096 name = name[:12]+".. " 

3097 sys.stdout.write("%-15s" % (name, )) 

3098 sys.stdout.flush() 

3099 windows = "?" 

3100 if state==DotXpra.LIVE: 

3101 dinfo = exec_and_parse("info", display) 

3102 if dinfo: 

3103 #first, find all the window properties: 

3104 winfo = {} 

3105 for k,v in dinfo.items(): 

3106 #ie: "windows.1.size-constraints.base-size" -> ["windows", "1", "size-constraints.base-size"] 

3107 parts = k.split(".", 2) 

3108 if parts[0]=="windows" and len(parts)==3: 

3109 winfo.setdefault(parts[1], {})[parts[2]] = v 

3110 #print("winfo=%s" % (winfo,)) 

3111 #then find a property we can show for each: 

3112 wstrs = [] 

3113 for props in winfo.values(): 

3114 for prop in ("command", "class-instance", "title"): 

3115 wstr = props.get(prop, "?") 

3116 if wstr and prop=="class-instance": 

3117 wstr = wstr.split("',")[0][2:] 

3118 if wstr and wstr!="?": 

3119 break 

3120 wstrs.append(wstr) 

3121 windows = csv(wstrs) 

3122 sys.stdout.write("%s\n" % (windows, )) 

3123 sys.stdout.flush() 

3124 

3125def run_auth(_options, args): 

3126 if not args: 

3127 raise InitException("missing module argument") 

3128 auth_str = args[0] 

3129 from xpra.server.auth.auth_helper import get_auth_module 

3130 auth, auth_module, _auth_class, _auth_options = get_auth_module(auth_str) 

3131 #see if the module has a "main" entry point: 

3132 main_fn = getattr(auth_module, "main", None) 

3133 if not main_fn: 

3134 raise InitExit(EXIT_UNSUPPORTED, "no command line utility for '%s' authentication module" % auth) 

3135 argv = [auth_module.__file__]+args[1:] 

3136 return main_fn(argv) 

3137 

3138 

3139def run_showconfig(options, args): 

3140 log = get_util_logger() 

3141 d = dict_to_validated_config({}) 

3142 fixup_options(d) 

3143 #this one is normally only probed at build time: 

3144 #(so probe it here again) 

3145 if POSIX: 

3146 try: 

3147 from xpra.platform.pycups_printing import get_printer_definition 

3148 for mimetype in ("pdf", "postscript"): 

3149 pdef = get_printer_definition(mimetype) 

3150 if pdef: 

3151 #ie: d.pdf_printer = "/usr/share/ppd/cupsfilters/Generic-PDF_Printer-PDF.ppd" 

3152 setattr(d, "%s_printer" % mimetype, pdef) 

3153 except Exception: 

3154 pass 

3155 VIRTUAL = ["mode"] #no such option! (it's a virtual one for the launch by config files) 

3156 #hide irrelevant options: 

3157 HIDDEN = [] 

3158 if not "all" in args: 

3159 #this logic probably belongs somewhere else: 

3160 if OSX or WIN32: 

3161 #these options don't make sense on win32 or osx: 

3162 HIDDEN += ["socket-dirs", "socket-dir", 

3163 "wm-name", "pulseaudio-command", "pulseaudio", "xvfb", "input-method", 

3164 "socket-permissions", "fake-xinerama", "dbus-proxy", "xsettings", 

3165 "exit-with-children", "start-new-commands", 

3166 "start", "start-child", 

3167 "start-after-connect", "start-child-after-connect", 

3168 "start-on-connect", "start-child-on-connect", 

3169 "start-on-last-client-exit", "start-child-on-last-client-exit", 

3170 "use-display", 

3171 ] 

3172 if WIN32: 

3173 #"exit-ssh"? 

3174 HIDDEN += ["lpadmin", "daemon", "mmap-group", "mdns"] 

3175 if not OSX: 

3176 HIDDEN += ["dock-icon", "swap-keys"] 

3177 for opt, otype in sorted(OPTION_TYPES.items()): 

3178 if opt in VIRTUAL: 

3179 continue 

3180 i = log.info 

3181 w = log.warn 

3182 if args: 

3183 if ("all" not in args) and (opt not in args): 

3184 continue 

3185 elif opt in HIDDEN: 

3186 i = log.debug 

3187 w = log.debug 

3188 k = name_to_field(opt) 

3189 dv = getattr(d, k) 

3190 cv = getattr(options, k, dv) 

3191 if cv!=dv: 

3192 w("%-20s (used) = %-32s %s", opt, vstr(otype, cv), type(cv)) 

3193 w("%-20s (default) = %-32s %s", opt, vstr(otype, dv), type(dv)) 

3194 else: 

3195 i("%-20s = %s", opt, vstr(otype, cv)) 

3196 

3197def vstr(otype, v) -> str: 

3198 #just used to quote all string values 

3199 if v is None: 

3200 if otype==bool: 

3201 return "auto" 

3202 return "" 

3203 if isinstance(v, str): 

3204 return "'%s'" % nonl(v) 

3205 if isinstance(v, (tuple, list)): 

3206 return csv(vstr(otype, x) for x in v) 

3207 return str(v) 

3208 

3209def run_showsetting(options, args): 

3210 if not args: 

3211 raise InitException("specify a setting to display") 

3212 

3213 log = get_util_logger() 

3214 

3215 settings = [] 

3216 for arg in args: 

3217 otype = OPTION_TYPES.get(arg) 

3218 if not otype: 

3219 log.warn("'%s' is not a valid setting", arg) 

3220 else: 

3221 settings.append(arg) 

3222 

3223 from xpra.platform import get_username 

3224 dirs = get_xpra_defaults_dirs(username=get_username(), uid=getuid(), gid=getgid()) 

3225 

3226 #default config: 

3227 config = get_defaults() 

3228 def show_settings(): 

3229 for setting in settings: 

3230 value = config.get(setting) 

3231 log.info("%-20s: %-40s (%s)", setting, vstr(None, value), type(value)) 

3232 

3233 log.info("* default config:") 

3234 show_settings() 

3235 for d in dirs: 

3236 config.clear() 

3237 config.update(read_xpra_conf(d)) 

3238 log.info("* '%s':", d) 

3239 show_settings() 

3240 return 0 

3241 

3242 

3243if __name__ == "__main__": # pragma: no cover 

3244 code = main("xpra.exe", sys.argv) 

3245 if not code: 

3246 code = 0 

3247 sys.exit(code)