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 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 | 124 |
| 125 # Thresholds for switching from fast- to slow-restart and for giving up | 125 # Thresholds for switching from fast- to slow-restart and for giving up |
| 126 # trying to restart entirely. | 126 # trying to restart entirely. |
| 127 SHORT_BACKOFF_THRESHOLD = 5 | 127 SHORT_BACKOFF_THRESHOLD = 5 |
| 128 MAX_LAUNCH_FAILURES = SHORT_BACKOFF_THRESHOLD + 10 | 128 MAX_LAUNCH_FAILURES = SHORT_BACKOFF_THRESHOLD + 10 |
| 129 | 129 |
| 130 # Number of seconds to save session output to the log. | 130 # Number of seconds to save session output to the log. |
| 131 SESSION_OUTPUT_TIME_LIMIT_SECONDS = 30 | 131 SESSION_OUTPUT_TIME_LIMIT_SECONDS = 30 |
| 132 | 132 |
| 133 # Globals needed by the atexit cleanup() handler. | 133 # Globals needed by the atexit cleanup() handler. |
| 134 g_desktops = [] | 134 g_desktop = None |
| 135 g_host_hash = hashlib.md5(socket.gethostname()).hexdigest() | 135 g_host_hash = hashlib.md5(socket.gethostname()).hexdigest() |
| 136 | 136 |
| 137 def gen_xorg_config(sizes): | 137 def gen_xorg_config(sizes): |
| 138 return ( | 138 return ( |
| 139 # This causes X to load the default GLX module, even if a proprietary one | 139 # This causes X to load the default GLX module, even if a proprietary one |
| 140 # is installed in a different directory. | 140 # is installed in a different directory. |
| 141 'Section "Files"\n' | 141 'Section "Files"\n' |
| 142 ' ModulePath "/usr/lib/xorg/modules"\n' | 142 ' ModulePath "/usr/lib/xorg/modules"\n' |
| 143 'EndSection\n' | 143 'EndSection\n' |
| 144 '\n' | 144 '\n' |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 401 self.host_proc = None | 401 self.host_proc = None |
| 402 self.child_env = None | 402 self.child_env = None |
| 403 self.sizes = sizes | 403 self.sizes = sizes |
| 404 self.xorg_conf = None | 404 self.xorg_conf = None |
| 405 self.pulseaudio_pipe = None | 405 self.pulseaudio_pipe = None |
| 406 self.server_supports_exact_resize = False | 406 self.server_supports_exact_resize = False |
| 407 self.server_supports_randr = False | 407 self.server_supports_randr = False |
| 408 self.randr_add_sizes = False | 408 self.randr_add_sizes = False |
| 409 self.host_ready = False | 409 self.host_ready = False |
| 410 self.ssh_auth_sockname = None | 410 self.ssh_auth_sockname = None |
| 411 g_desktops.append(self) | 411 g_desktop = self |
|
Lambros
2017/05/18 20:39:59
optional: Maybe assert() or raise an error if g_de
| |
| 412 | 412 |
| 413 @staticmethod | 413 @staticmethod |
| 414 def get_unused_display_number(): | 414 def get_unused_display_number(): |
| 415 """Return a candidate display number for which there is currently no | 415 """Return a candidate display number for which there is currently no |
| 416 X Server lock file""" | 416 X Server lock file""" |
| 417 display = FIRST_X_DISPLAY_NUMBER | 417 display = FIRST_X_DISPLAY_NUMBER |
| 418 while os.path.exists(X_LOCK_FILE_TEMPLATE % display): | 418 while os.path.exists(X_LOCK_FILE_TEMPLATE % display): |
| 419 display += 1 | 419 display += 1 |
| 420 return display | 420 return display |
| 421 | 421 |
| (...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 744 if self.ssh_auth_sockname: | 744 if self.ssh_auth_sockname: |
| 745 args.append("--ssh-auth-sockname=%s" % self.ssh_auth_sockname) | 745 args.append("--ssh-auth-sockname=%s" % self.ssh_auth_sockname) |
| 746 | 746 |
| 747 args.extend(extra_start_host_args) | 747 args.extend(extra_start_host_args) |
| 748 | 748 |
| 749 # Have the host process use SIGUSR1 to signal a successful start. | 749 # Have the host process use SIGUSR1 to signal a successful start. |
| 750 def sigusr1_handler(signum, frame): | 750 def sigusr1_handler(signum, frame): |
| 751 _ = signum, frame | 751 _ = signum, frame |
| 752 logging.info("Host ready to receive connections.") | 752 logging.info("Host ready to receive connections.") |
| 753 self.host_ready = True | 753 self.host_ready = True |
| 754 if (ParentProcessLogger.instance() and | 754 if (ParentProcessLogger.instance() and g_desktop is not None and |
| 755 False not in [desktop.host_ready for desktop in g_desktops]): | 755 g_desktop.host_ready): |
| 756 ParentProcessLogger.instance().release_parent(True) | 756 ParentProcessLogger.instance().release_parent(True) |
| 757 | 757 |
| 758 signal.signal(signal.SIGUSR1, sigusr1_handler) | 758 signal.signal(signal.SIGUSR1, sigusr1_handler) |
| 759 args.append("--signal-parent") | 759 args.append("--signal-parent") |
| 760 | 760 |
| 761 logging.info(args) | 761 logging.info(args) |
| 762 self.host_proc = subprocess.Popen(args, env=self.child_env, | 762 self.host_proc = subprocess.Popen(args, env=self.child_env, |
| 763 stdin=subprocess.PIPE) | 763 stdin=subprocess.PIPE) |
| 764 if not self.host_proc.pid: | 764 if not self.host_proc.pid: |
| 765 raise Exception("Could not start Chrome Remote Desktop host") | 765 raise Exception("Could not start Chrome Remote Desktop host") |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1056 os.dup2(log_fd, sys.stderr.fileno()) | 1056 os.dup2(log_fd, sys.stderr.fileno()) |
| 1057 | 1057 |
| 1058 # Close the temporary file-descriptors. | 1058 # Close the temporary file-descriptors. |
| 1059 os.close(devnull_fd) | 1059 os.close(devnull_fd) |
| 1060 os.close(log_fd) | 1060 os.close(log_fd) |
| 1061 | 1061 |
| 1062 | 1062 |
| 1063 def cleanup(): | 1063 def cleanup(): |
| 1064 logging.info("Cleanup.") | 1064 logging.info("Cleanup.") |
| 1065 | 1065 |
| 1066 global g_desktops | 1066 global g_desktop |
| 1067 for desktop in g_desktops: | 1067 if g_desktop is not None: |
| 1068 for proc, name in [(desktop.x_proc, "X server"), | 1068 for proc, name in [(g_desktop.x_proc, "X server"), |
| 1069 (desktop.session_proc, "session"), | 1069 (g_desktop.session_proc, "session"), |
| 1070 (desktop.host_proc, "host")]: | 1070 (g_desktop.host_proc, "host")]: |
| 1071 if proc is not None: | 1071 if proc is not None: |
| 1072 logging.info("Terminating " + name) | 1072 logging.info("Terminating " + name) |
| 1073 try: | 1073 try: |
| 1074 psutil_proc = psutil.Process(proc.pid) | 1074 psutil_proc = psutil.Process(proc.pid) |
| 1075 psutil_proc.terminate() | 1075 psutil_proc.terminate() |
| 1076 | 1076 |
| 1077 # Use a short timeout, to avoid delaying service shutdown if the | 1077 # Use a short timeout, to avoid delaying service shutdown if the |
| 1078 # process refuses to die for some reason. | 1078 # process refuses to die for some reason. |
| 1079 psutil_proc.wait(timeout=10) | 1079 psutil_proc.wait(timeout=10) |
| 1080 except psutil.TimeoutExpired: | 1080 except psutil.TimeoutExpired: |
| 1081 logging.error("Timed out - sending SIGKILL") | 1081 logging.error("Timed out - sending SIGKILL") |
| 1082 psutil_proc.kill() | 1082 psutil_proc.kill() |
| 1083 except psutil.Error: | 1083 except psutil.Error: |
| 1084 logging.error("Error terminating process") | 1084 logging.error("Error terminating process") |
| 1085 if desktop.xorg_conf is not None: | 1085 if g_desktop.xorg_conf is not None: |
| 1086 os.remove(desktop.xorg_conf) | 1086 os.remove(g_desktop.xorg_conf) |
| 1087 | 1087 |
| 1088 g_desktops = [] | 1088 g_desktop = None |
| 1089 if ParentProcessLogger.instance(): | 1089 if ParentProcessLogger.instance(): |
| 1090 ParentProcessLogger.instance().release_parent(False) | 1090 ParentProcessLogger.instance().release_parent(False) |
| 1091 | 1091 |
| 1092 class SignalHandler: | 1092 class SignalHandler: |
| 1093 """Reload the config file on SIGHUP. Since we pass the configuration to the | 1093 """Reload the config file on SIGHUP. Since we pass the configuration to the |
| 1094 host processes via stdin, they can't reload it, so terminate them. They will | 1094 host processes via stdin, they can't reload it, so terminate them. They will |
| 1095 be relaunched automatically with the new config.""" | 1095 be relaunched automatically with the new config.""" |
| 1096 | 1096 |
| 1097 def __init__(self, host_config): | 1097 def __init__(self, host_config): |
| 1098 self.host_config = host_config | 1098 self.host_config = host_config |
| 1099 | 1099 |
| 1100 def __call__(self, signum, _stackframe): | 1100 def __call__(self, signum, _stackframe): |
| 1101 if signum == signal.SIGHUP: | 1101 if signum == signal.SIGHUP: |
| 1102 logging.info("SIGHUP caught, restarting host.") | 1102 logging.info("SIGHUP caught, restarting host.") |
| 1103 try: | 1103 try: |
| 1104 self.host_config.load() | 1104 self.host_config.load() |
| 1105 except (IOError, ValueError) as e: | 1105 except (IOError, ValueError) as e: |
| 1106 logging.error("Failed to load config: " + str(e)) | 1106 logging.error("Failed to load config: " + str(e)) |
| 1107 for desktop in g_desktops: | 1107 if g_desktop is not None: |
| 1108 if desktop.host_proc: | 1108 if g_desktop.host_proc: |
| 1109 desktop.host_proc.send_signal(signal.SIGTERM) | 1109 g_desktop.host_proc.send_signal(signal.SIGTERM) |
| 1110 else: | 1110 else: |
| 1111 # Exit cleanly so the atexit handler, cleanup(), gets called. | 1111 # Exit cleanly so the atexit handler, cleanup(), gets called. |
| 1112 raise SystemExit | 1112 raise SystemExit |
| 1113 | 1113 |
| 1114 | 1114 |
| 1115 class RelaunchInhibitor: | 1115 class RelaunchInhibitor: |
| 1116 """Helper class for inhibiting launch of a child process before a timeout has | 1116 """Helper class for inhibiting launch of a child process before a timeout has |
| 1117 elapsed. | 1117 elapsed. |
| 1118 | 1118 |
| 1119 A managed process can be in one of these states: | 1119 A managed process can be in one of these states: |
| (...skipping 501 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1621 else: | 1621 else: |
| 1622 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) | 1622 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) |
| 1623 elif os.WIFSIGNALED(status): | 1623 elif os.WIFSIGNALED(status): |
| 1624 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) | 1624 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) |
| 1625 | 1625 |
| 1626 | 1626 |
| 1627 if __name__ == "__main__": | 1627 if __name__ == "__main__": |
| 1628 logging.basicConfig(level=logging.DEBUG, | 1628 logging.basicConfig(level=logging.DEBUG, |
| 1629 format="%(asctime)s:%(levelname)s:%(message)s") | 1629 format="%(asctime)s:%(levelname)s:%(message)s") |
| 1630 sys.exit(main()) | 1630 sys.exit(main()) |
| OLD | NEW |