Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # Virtual Me2Me implementation. This script runs and manages the processes | 6 # Virtual Me2Me implementation. This script runs and manages the processes |
| 7 # required for a Virtual Me2Me desktop, which are: X server, X desktop | 7 # required for a Virtual Me2Me desktop, which are: X server, X desktop |
| 8 # session, and Host process. | 8 # session, and Host process. |
| 9 # This script is intended to run continuously as a background daemon | 9 # This script is intended to run continuously as a background daemon |
| 10 # process, running under an ordinary (non-root) user account. | 10 # process, running under an ordinary (non-root) user account. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 48 # defaults can be overridden in ~/.profile. | 48 # defaults can be overridden in ~/.profile. |
| 49 DEFAULT_SIZES = "1600x1200,3840x2560" | 49 DEFAULT_SIZES = "1600x1200,3840x2560" |
| 50 | 50 |
| 51 # If RANDR is not available, use a smaller default size. Only a single | 51 # If RANDR is not available, use a smaller default size. Only a single |
| 52 # resolution is supported in this case. | 52 # resolution is supported in this case. |
| 53 DEFAULT_SIZE_NO_RANDR = "1600x1200" | 53 DEFAULT_SIZE_NO_RANDR = "1600x1200" |
| 54 | 54 |
| 55 SCRIPT_PATH = os.path.abspath(sys.argv[0]) | 55 SCRIPT_PATH = os.path.abspath(sys.argv[0]) |
| 56 SCRIPT_DIR = os.path.dirname(SCRIPT_PATH) | 56 SCRIPT_DIR = os.path.dirname(SCRIPT_PATH) |
| 57 | 57 |
| 58 IS_INSTALLED = (os.path.basename(sys.argv[0]) != 'linux_me2me_host.py') | 58 HOST_BINARY_NAME = "chrome-remote-desktop-host" |
| 59 | |
| 60 if IS_INSTALLED: | |
| 61 HOST_BINARY_NAME = "chrome-remote-desktop-host" | |
| 62 else: | |
| 63 HOST_BINARY_NAME = "remoting_me2me_host" | |
| 64 | |
| 65 CHROME_REMOTING_GROUP_NAME = "chrome-remote-desktop" | 59 CHROME_REMOTING_GROUP_NAME = "chrome-remote-desktop" |
| 66 | 60 |
| 67 HOME_DIR = os.environ["HOME"] | 61 HOME_DIR = os.environ["HOME"] |
| 68 CONFIG_DIR = os.path.join(HOME_DIR, ".config/chrome-remote-desktop") | 62 CONFIG_DIR = os.path.join(HOME_DIR, ".config/chrome-remote-desktop") |
| 69 SESSION_FILE_PATH = os.path.join(HOME_DIR, ".chrome-remote-desktop-session") | 63 SESSION_FILE_PATH = os.path.join(HOME_DIR, ".chrome-remote-desktop-session") |
| 70 SYSTEM_SESSION_FILE_PATH = "/etc/chrome-remote-desktop-session" | 64 SYSTEM_SESSION_FILE_PATH = "/etc/chrome-remote-desktop-session" |
| 71 | 65 |
| 72 X_LOCK_FILE_TEMPLATE = "/tmp/.X%d-lock" | 66 X_LOCK_FILE_TEMPLATE = "/tmp/.X%d-lock" |
| 73 FIRST_X_DISPLAY_NUMBER = 20 | 67 FIRST_X_DISPLAY_NUMBER = 20 |
| 74 | 68 |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 98 return True | 92 return True |
| 99 | 93 |
| 100 # The host has been tested only on Ubuntu. | 94 # The host has been tested only on Ubuntu. |
| 101 distribution = platform.linux_distribution() | 95 distribution = platform.linux_distribution() |
| 102 return (distribution[0]).lower() == 'ubuntu' | 96 return (distribution[0]).lower() == 'ubuntu' |
| 103 | 97 |
| 104 | 98 |
| 105 def get_randr_supporting_x_server(): | 99 def get_randr_supporting_x_server(): |
| 106 """Returns a path to an X server that supports the RANDR extension, if this | 100 """Returns a path to an X server that supports the RANDR extension, if this |
| 107 is found on the system. Otherwise returns None.""" | 101 is found on the system. Otherwise returns None.""" |
| 108 try: | 102 |
| 109 xvfb = "/usr/bin/Xvfb-randr" | 103 xvfb = "/usr/bin/Xvfb-randr" |
| 110 if not os.path.exists(xvfb): | 104 if os.path.exists(xvfb): |
| 111 xvfb = locate_executable("Xvfb-randr") | |
| 112 return xvfb | 105 return xvfb |
| 113 except Exception: | 106 |
| 114 return None | 107 xvfb = os.path.join(SCRIPT_DIR, "Xvfb-randr") |
| 108 if os.path.exists(xvfb): | |
| 109 return xvfb | |
| 115 | 110 |
|
Sergey Ulanov
2016/04/18 23:58:36
add return None here. get() function without retur
Lambros
2016/04/19 01:51:29
Done.
| |
| 116 | 111 |
| 117 class Config: | 112 class Config: |
| 118 def __init__(self, path): | 113 def __init__(self, path): |
| 119 self.path = path | 114 self.path = path |
| 120 self.data = {} | 115 self.data = {} |
| 121 self.changed = False | 116 self.changed = False |
| 122 | 117 |
| 123 def load(self): | 118 def load(self): |
| 124 """Loads the config from file. | 119 """Loads the config from file. |
| 125 | 120 |
| (...skipping 364 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 490 | 485 |
| 491 def launch_session(self, x_args): | 486 def launch_session(self, x_args): |
| 492 self._init_child_env() | 487 self._init_child_env() |
| 493 self._setup_pulseaudio() | 488 self._setup_pulseaudio() |
| 494 self._setup_gnubby() | 489 self._setup_gnubby() |
| 495 self._launch_x_server(x_args) | 490 self._launch_x_server(x_args) |
| 496 self._launch_x_session() | 491 self._launch_x_session() |
| 497 | 492 |
| 498 def launch_host(self, host_config): | 493 def launch_host(self, host_config): |
| 499 # Start remoting host | 494 # Start remoting host |
| 500 args = [locate_executable(HOST_BINARY_NAME), "--host-config=-"] | 495 host_path = os.path.join(SCRIPT_DIR, HOST_BINARY_NAME) |
| 496 args = [host_path, "--host-config=-"] | |
| 501 if self.pulseaudio_pipe: | 497 if self.pulseaudio_pipe: |
| 502 args.append("--audio-pipe-name=%s" % self.pulseaudio_pipe) | 498 args.append("--audio-pipe-name=%s" % self.pulseaudio_pipe) |
| 503 if self.server_supports_exact_resize: | 499 if self.server_supports_exact_resize: |
| 504 args.append("--server-supports-exact-resize") | 500 args.append("--server-supports-exact-resize") |
| 505 if self.ssh_auth_sockname: | 501 if self.ssh_auth_sockname: |
| 506 args.append("--ssh-auth-sockname=%s" % self.ssh_auth_sockname) | 502 args.append("--ssh-auth-sockname=%s" % self.ssh_auth_sockname) |
| 507 | 503 |
| 508 # Have the host process use SIGUSR1 to signal a successful start. | 504 # Have the host process use SIGUSR1 to signal a successful start. |
| 509 def sigusr1_handler(signum, frame): | 505 def sigusr1_handler(signum, frame): |
| 510 _ = signum, frame | 506 _ = signum, frame |
| 511 logging.info("Host ready to receive connections.") | 507 logging.info("Host ready to receive connections.") |
| 512 self.host_ready = True | 508 self.host_ready = True |
| 513 if (ParentProcessLogger.instance() and | 509 if (ParentProcessLogger.instance() and |
| 514 False not in [desktop.host_ready for desktop in g_desktops]): | 510 False not in [desktop.host_ready for desktop in g_desktops]): |
| 515 ParentProcessLogger.instance().release_parent(True) | 511 ParentProcessLogger.instance().release_parent(True) |
| 516 | 512 |
| 517 signal.signal(signal.SIGUSR1, sigusr1_handler) | 513 signal.signal(signal.SIGUSR1, sigusr1_handler) |
| 518 args.append("--signal-parent") | 514 args.append("--signal-parent") |
| 519 | 515 |
| 516 logging.info(args) | |
| 520 self.host_proc = subprocess.Popen(args, env=self.child_env, | 517 self.host_proc = subprocess.Popen(args, env=self.child_env, |
| 521 stdin=subprocess.PIPE) | 518 stdin=subprocess.PIPE) |
| 522 logging.info(args) | |
| 523 if not self.host_proc.pid: | 519 if not self.host_proc.pid: |
| 524 raise Exception("Could not start Chrome Remote Desktop host") | 520 raise Exception("Could not start Chrome Remote Desktop host") |
| 525 | 521 |
| 526 try: | 522 try: |
| 527 self.host_proc.stdin.write(json.dumps(host_config.data).encode('UTF-8')) | 523 self.host_proc.stdin.write(json.dumps(host_config.data).encode('UTF-8')) |
| 528 self.host_proc.stdin.flush() | 524 self.host_proc.stdin.flush() |
| 529 except IOError as e: | 525 except IOError as e: |
| 530 # This can occur in rare situations, for example, if the machine is | 526 # This can occur in rare situations, for example, if the machine is |
| 531 # heavily loaded and the host process dies quickly (maybe if the X | 527 # heavily loaded and the host process dies quickly (maybe if the X |
| 532 # connection failed), the host process might be gone before this code | 528 # connection failed), the host process might be gone before this code |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 623 # corrupt displays). So if the ubuntu-2d session is available, | 619 # corrupt displays). So if the ubuntu-2d session is available, |
| 624 # choose it explicitly. | 620 # choose it explicitly. |
| 625 return [session_wrapper, "/usr/bin/gnome-session --session=ubuntu-2d"] | 621 return [session_wrapper, "/usr/bin/gnome-session --session=ubuntu-2d"] |
| 626 else: | 622 else: |
| 627 # Use the session wrapper by itself, and let the system choose a | 623 # Use the session wrapper by itself, and let the system choose a |
| 628 # session. | 624 # session. |
| 629 return [session_wrapper] | 625 return [session_wrapper] |
| 630 return None | 626 return None |
| 631 | 627 |
| 632 | 628 |
| 633 def locate_executable(exe_name): | |
| 634 if IS_INSTALLED: | |
| 635 # If the script is running from its installed location, search the host | |
| 636 # binary only in the same directory. | |
| 637 paths_to_try = [ SCRIPT_DIR ] | |
| 638 else: | |
| 639 paths_to_try = map(lambda p: os.path.join(SCRIPT_DIR, p), | |
| 640 [".", | |
| 641 "../../../out/Debug", | |
| 642 "../../../out/Default", | |
| 643 "../../../out/Release"]) | |
| 644 for path in paths_to_try: | |
| 645 exe_path = os.path.join(path, exe_name) | |
| 646 if os.path.exists(exe_path): | |
| 647 return exe_path | |
| 648 | |
| 649 raise Exception("Could not locate executable '%s'" % exe_name) | |
| 650 | |
| 651 | |
| 652 class ParentProcessLogger(object): | 629 class ParentProcessLogger(object): |
| 653 """Redirects logs to the parent process, until the host is ready or quits. | 630 """Redirects logs to the parent process, until the host is ready or quits. |
| 654 | 631 |
| 655 This class creates a pipe to allow logging from the daemon process to be | 632 This class creates a pipe to allow logging from the daemon process to be |
| 656 copied to the parent process. The daemon process adds a log-handler that | 633 copied to the parent process. The daemon process adds a log-handler that |
| 657 directs logging output to the pipe. The parent process reads from this pipe | 634 directs logging output to the pipe. The parent process reads from this pipe |
| 658 until and writes the content to stderr. When the pipe is no longer needed | 635 until and writes the content to stderr. When the pipe is no longer needed |
| 659 (for example, the host signals successful launch or permanent failure), the | 636 (for example, the host signals successful launch or permanent failure), the |
| 660 daemon removes the log-handler and closes the pipe, causing the the parent | 637 daemon removes the log-handler and closes the pipe, causing the the parent |
| 661 process to reach end-of-file while reading the pipe and exit. | 638 process to reach end-of-file while reading the pipe and exit. |
| (...skipping 521 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1183 subprocess.check_call(["/usr/sbin/groupadd", "-f", | 1160 subprocess.check_call(["/usr/sbin/groupadd", "-f", |
| 1184 CHROME_REMOTING_GROUP_NAME]) | 1161 CHROME_REMOTING_GROUP_NAME]) |
| 1185 subprocess.check_call(["/usr/bin/gpasswd", "--add", user, | 1162 subprocess.check_call(["/usr/bin/gpasswd", "--add", user, |
| 1186 CHROME_REMOTING_GROUP_NAME]) | 1163 CHROME_REMOTING_GROUP_NAME]) |
| 1187 except (ValueError, OSError, subprocess.CalledProcessError) as e: | 1164 except (ValueError, OSError, subprocess.CalledProcessError) as e: |
| 1188 logging.error("Command failed: " + str(e)) | 1165 logging.error("Command failed: " + str(e)) |
| 1189 return 1 | 1166 return 1 |
| 1190 | 1167 |
| 1191 return 0 | 1168 return 0 |
| 1192 | 1169 |
| 1193 if options.host_version: | 1170 if options.host_version: |
|
Sergey Ulanov
2016/04/18 23:58:36
I don't think we need this flag anymore (it was us
Lambros
2016/04/19 01:51:29
Flag removed in https://codereview.chromium.org/18
| |
| 1194 # TODO(sergeyu): Also check RPM package version once we add RPM package. | 1171 # TODO(sergeyu): Also check RPM package version once we add RPM package. |
| 1195 return os.system(locate_executable(HOST_BINARY_NAME) + " --version") >> 8 | 1172 host_path = os.path.join(SCRIPT_DIR, HOST_BINARY_NAME) |
|
Sergey Ulanov
2016/04/18 23:58:36
If we still need the flag, then this expression is
Lambros
2016/04/19 01:51:29
Acknowledged.
| |
| 1173 return os.system(host_path + " --version") >> 8 | |
| 1196 | 1174 |
| 1197 if options.watch_resolution: | 1175 if options.watch_resolution: |
| 1198 watch_for_resolution_changes(options.watch_resolution) | 1176 watch_for_resolution_changes(options.watch_resolution) |
| 1199 return 0 | 1177 return 0 |
| 1200 | 1178 |
| 1201 if not options.start: | 1179 if not options.start: |
| 1202 # If no modal command-line options specified, print an error and exit. | 1180 # If no modal command-line options specified, print an error and exit. |
| 1203 print(EPILOG, file=sys.stderr) | 1181 print(EPILOG, file=sys.stderr) |
| 1204 return 1 | 1182 return 1 |
| 1205 | 1183 |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1399 else: | 1377 else: |
| 1400 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) | 1378 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) |
| 1401 elif os.WIFSIGNALED(status): | 1379 elif os.WIFSIGNALED(status): |
| 1402 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) | 1380 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) |
| 1403 | 1381 |
| 1404 | 1382 |
| 1405 if __name__ == "__main__": | 1383 if __name__ == "__main__": |
| 1406 logging.basicConfig(level=logging.DEBUG, | 1384 logging.basicConfig(level=logging.DEBUG, |
| 1407 format="%(asctime)s:%(levelname)s:%(message)s") | 1385 format="%(asctime)s:%(levelname)s:%(message)s") |
| 1408 sys.exit(main()) | 1386 sys.exit(main()) |
| OLD | NEW |