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 |