 Chromium Code Reviews
 Chromium Code Reviews Issue 1685793003:
  Add message from grandchild to parent to indicate successful host launch.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1685793003:
  Add message from grandchild to parent to indicate successful host launch.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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 494 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 505 if self.ssh_auth_sockname: | 505 if self.ssh_auth_sockname: | 
| 506 args.append("--ssh-auth-sockname=%s" % self.ssh_auth_sockname) | 506 args.append("--ssh-auth-sockname=%s" % self.ssh_auth_sockname) | 
| 507 | 507 | 
| 508 # Have the host process use SIGUSR1 to signal a successful start. | 508 # Have the host process use SIGUSR1 to signal a successful start. | 
| 509 def sigusr1_handler(signum, frame): | 509 def sigusr1_handler(signum, frame): | 
| 510 _ = signum, frame | 510 _ = signum, frame | 
| 511 logging.info("Host ready to receive connections.") | 511 logging.info("Host ready to receive connections.") | 
| 512 self.host_ready = True | 512 self.host_ready = True | 
| 513 if (ParentProcessLogger.instance() and | 513 if (ParentProcessLogger.instance() and | 
| 514 False not in [desktop.host_ready for desktop in g_desktops]): | 514 False not in [desktop.host_ready for desktop in g_desktops]): | 
| 515 ParentProcessLogger.instance().release_parent() | 515 ParentProcessLogger.instance().release_parent(True) | 
| 516 | 516 | 
| 517 signal.signal(signal.SIGUSR1, sigusr1_handler) | 517 signal.signal(signal.SIGUSR1, sigusr1_handler) | 
| 518 args.append("--signal-parent") | 518 args.append("--signal-parent") | 
| 519 | 519 | 
| 520 self.host_proc = subprocess.Popen(args, env=self.child_env, | 520 self.host_proc = subprocess.Popen(args, env=self.child_env, | 
| 521 stdin=subprocess.PIPE) | 521 stdin=subprocess.PIPE) | 
| 522 logging.info(args) | 522 logging.info(args) | 
| 523 if not self.host_proc.pid: | 523 if not self.host_proc.pid: | 
| 524 raise Exception("Could not start Chrome Remote Desktop host") | 524 raise Exception("Could not start Chrome Remote Desktop host") | 
| 525 | 525 | 
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 675 # Ensure write_pipe is closed on exec, otherwise it will be kept open by | 675 # Ensure write_pipe is closed on exec, otherwise it will be kept open by | 
| 676 # child processes (X, host), preventing the read pipe from EOF'ing. | 676 # child processes (X, host), preventing the read pipe from EOF'ing. | 
| 677 old_flags = fcntl.fcntl(write_pipe, fcntl.F_GETFD) | 677 old_flags = fcntl.fcntl(write_pipe, fcntl.F_GETFD) | 
| 678 fcntl.fcntl(write_pipe, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC) | 678 fcntl.fcntl(write_pipe, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC) | 
| 679 self._read_file = os.fdopen(read_pipe, 'r') | 679 self._read_file = os.fdopen(read_pipe, 'r') | 
| 680 self._write_file = os.fdopen(write_pipe, 'w') | 680 self._write_file = os.fdopen(write_pipe, 'w') | 
| 681 self._logging_handler = None | 681 self._logging_handler = None | 
| 682 ParentProcessLogger.__instance = self | 682 ParentProcessLogger.__instance = self | 
| 683 | 683 | 
| 684 def start_logging(self): | 684 def start_logging(self): | 
| 685 """Installs a logging handler that sends log entries to a pipe. | 685 """Installs a logging handler that sends log entries to a pipe, prefixed | 
| 686 with the string 'MSG:'. This allows them to be distinguished by the parent | |
| 687 process from commands sent over the same pipe. | |
| 686 | 688 | 
| 687 Must be called by the child process. | 689 Must be called by the child process. | 
| 688 """ | 690 """ | 
| 689 self._read_file.close() | 691 self._read_file.close() | 
| 690 self._logging_handler = logging.StreamHandler(self._write_file) | 692 self._logging_handler = logging.StreamHandler(self._write_file) | 
| 693 self._logging_handler.setFormatter(logging.Formatter(fmt='MSG:%(message)s')) | |
| 691 logging.getLogger().addHandler(self._logging_handler) | 694 logging.getLogger().addHandler(self._logging_handler) | 
| 692 | 695 | 
| 693 def release_parent(self): | 696 def release_parent(self, success): | 
| 694 """Uninstalls logging handler and closes the pipe, releasing the parent. | 697 """Uninstalls logging handler and closes the pipe, releasing the parent. | 
| 695 | 698 | 
| 696 Must be called by the child process. | 699 Must be called by the child process. | 
| 700 | |
| 701 success: If true, write a "host ready" message to the parent process before | |
| 702 closing the pipe. | |
| 697 """ | 703 """ | 
| 698 if self._logging_handler: | 704 if self._logging_handler: | 
| 699 logging.getLogger().removeHandler(self._logging_handler) | 705 logging.getLogger().removeHandler(self._logging_handler) | 
| 700 self._logging_handler = None | 706 self._logging_handler = None | 
| 701 if not self._write_file.closed: | 707 if not self._write_file.closed: | 
| 708 if success: | |
| 
Jamie
2016/02/11 00:00:23
Unrelated to your comments--I forgot to include th
 | |
| 709 self._write_file.write("READY\n") | |
| 710 self._write_file.flush() | |
| 702 self._write_file.close() | 711 self._write_file.close() | 
| 703 | 712 | 
| 704 def wait_for_logs(self): | 713 def wait_for_logs(self): | 
| 705 """Waits and prints log lines from the daemon until the pipe is closed. | 714 """Waits and prints log lines from the daemon until the pipe is closed. | 
| 706 | 715 | 
| 707 Must be called by the parent process. | 716 Must be called by the parent process. | 
| 717 | |
| 718 Returns: | |
| 719 True if the host started and successfully registered with the directory; | |
| 720 false otherwise. | |
| 708 """ | 721 """ | 
| 709 # If Ctrl-C is pressed, inform the user that the daemon is still running. | 722 # If Ctrl-C is pressed, inform the user that the daemon is still running. | 
| 710 # This signal will cause the read loop below to stop with an EINTR IOError. | 723 # This signal will cause the read loop below to stop with an EINTR IOError. | 
| 711 def sigint_handler(signum, frame): | 724 def sigint_handler(signum, frame): | 
| 712 _ = signum, frame | 725 _ = signum, frame | 
| 713 print("Interrupted. The daemon is still running in the background.", | 726 print("Interrupted. The daemon is still running in the background.", | 
| 714 file=sys.stderr) | 727 file=sys.stderr) | 
| 715 | 728 | 
| 716 signal.signal(signal.SIGINT, sigint_handler) | 729 signal.signal(signal.SIGINT, sigint_handler) | 
| 717 | 730 | 
| 718 # Install a fallback timeout to release the parent process, in case the | 731 # Install a fallback timeout to release the parent process, in case the | 
| 719 # daemon never responds (e.g. host crash-looping, daemon killed). | 732 # daemon never responds (e.g. host crash-looping, daemon killed). | 
| 720 # This signal will cause the read loop below to stop with an EINTR IOError. | 733 # This signal will cause the read loop below to stop with an EINTR IOError. | 
| 721 def sigalrm_handler(signum, frame): | 734 def sigalrm_handler(signum, frame): | 
| 722 _ = signum, frame | 735 _ = signum, frame | 
| 723 print("No response from daemon. It may have crashed, or may still be " | 736 print("No response from daemon. It may have crashed, or may still be " | 
| 724 "running in the background.", file=sys.stderr) | 737 "running in the background.", file=sys.stderr) | 
| 725 | 738 | 
| 726 signal.signal(signal.SIGALRM, sigalrm_handler) | 739 signal.signal(signal.SIGALRM, sigalrm_handler) | 
| 727 signal.alarm(30) | 740 signal.alarm(30) | 
| 728 | 741 | 
| 729 self._write_file.close() | 742 self._write_file.close() | 
| 730 | 743 | 
| 731 # Print lines as they're logged to the pipe until EOF is reached or readline | 744 # Print lines as they're logged to the pipe until EOF is reached or readline | 
| 732 # is interrupted by one of the signal handlers above. | 745 # is interrupted by one of the signal handlers above. | 
| 746 host_ready = False | |
| 733 try: | 747 try: | 
| 734 for line in iter(self._read_file.readline, ''): | 748 for line in iter(self._read_file.readline, ''): | 
| 735 sys.stderr.write(line) | 749 if line[:4] == "MSG:": | 
| 750 sys.stderr.write(line[4:]) | |
| 751 elif line == "READY\n": | |
| 752 host_ready = True | |
| 753 else: | |
| 754 sys.stderr.write("Unrecognized command: " + line) | |
| 736 except IOError as e: | 755 except IOError as e: | 
| 737 if e.errno != errno.EINTR: | 756 if e.errno != errno.EINTR: | 
| 738 raise | 757 raise | 
| 739 print("Log file: %s" % os.environ[LOG_FILE_ENV_VAR], file=sys.stderr) | 758 print("Log file: %s" % os.environ[LOG_FILE_ENV_VAR], file=sys.stderr) | 
| 759 return host_ready | |
| 740 | 760 | 
| 741 @staticmethod | 761 @staticmethod | 
| 742 def instance(): | 762 def instance(): | 
| 743 """Returns the singleton instance, if it exists.""" | 763 """Returns the singleton instance, if it exists.""" | 
| 744 return ParentProcessLogger.__instance | 764 return ParentProcessLogger.__instance | 
| 745 | 765 | 
| 746 | 766 | 
| 747 def daemonize(): | 767 def daemonize(): | 
| 748 """Background this process and detach from controlling terminal, redirecting | 768 """Background this process and detach from controlling terminal, redirecting | 
| 749 stdout/stderr to a log file.""" | 769 stdout/stderr to a log file.""" | 
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 788 pid = os.fork() | 808 pid = os.fork() | 
| 789 | 809 | 
| 790 if pid == 0: | 810 if pid == 0: | 
| 791 # Grandchild process | 811 # Grandchild process | 
| 792 pass | 812 pass | 
| 793 else: | 813 else: | 
| 794 # Child process | 814 # Child process | 
| 795 os._exit(0) # pylint: disable=W0212 | 815 os._exit(0) # pylint: disable=W0212 | 
| 796 else: | 816 else: | 
| 797 # Parent process | 817 # Parent process | 
| 798 parent_logger.wait_for_logs() | 818 if parent_logger.wait_for_logs(): | 
| 799 os._exit(0) # pylint: disable=W0212 | 819 os._exit(0) # pylint: disable=W0212 | 
| 820 else: | |
| 821 os._exit(1) # pylint: disable=W0212 | |
| 800 | 822 | 
| 801 logging.info("Daemon process started in the background, logging to '%s'" % | 823 logging.info("Daemon process started in the background, logging to '%s'" % | 
| 802 os.environ[LOG_FILE_ENV_VAR]) | 824 os.environ[LOG_FILE_ENV_VAR]) | 
| 803 | 825 | 
| 804 os.chdir(HOME_DIR) | 826 os.chdir(HOME_DIR) | 
| 805 | 827 | 
| 806 parent_logger.start_logging() | 828 parent_logger.start_logging() | 
| 807 | 829 | 
| 808 # Copy the file-descriptors to create new stdin, stdout and stderr. Note | 830 # Copy the file-descriptors to create new stdin, stdout and stderr. Note | 
| 809 # that dup2(oldfd, newfd) closes newfd first, so this will close the current | 831 # that dup2(oldfd, newfd) closes newfd first, so this will close the current | 
| (...skipping 25 matching lines...) Expand all Loading... | |
| 835 # process refuses to die for some reason. | 857 # process refuses to die for some reason. | 
| 836 psutil_proc.wait(timeout=10) | 858 psutil_proc.wait(timeout=10) | 
| 837 except psutil.TimeoutExpired: | 859 except psutil.TimeoutExpired: | 
| 838 logging.error("Timed out - sending SIGKILL") | 860 logging.error("Timed out - sending SIGKILL") | 
| 839 psutil_proc.kill() | 861 psutil_proc.kill() | 
| 840 except psutil.Error: | 862 except psutil.Error: | 
| 841 logging.error("Error terminating process") | 863 logging.error("Error terminating process") | 
| 842 | 864 | 
| 843 g_desktops = [] | 865 g_desktops = [] | 
| 844 if ParentProcessLogger.instance(): | 866 if ParentProcessLogger.instance(): | 
| 845 ParentProcessLogger.instance().release_parent() | 867 ParentProcessLogger.instance().release_parent(False) | 
| 846 | 868 | 
| 847 class SignalHandler: | 869 class SignalHandler: | 
| 848 """Reload the config file on SIGHUP. Since we pass the configuration to the | 870 """Reload the config file on SIGHUP. Since we pass the configuration to the | 
| 849 host processes via stdin, they can't reload it, so terminate them. They will | 871 host processes via stdin, they can't reload it, so terminate them. They will | 
| 850 be relaunched automatically with the new config.""" | 872 be relaunched automatically with the new config.""" | 
| 851 | 873 | 
| 852 def __init__(self, host_config): | 874 def __init__(self, host_config): | 
| 853 self.host_config = host_config | 875 self.host_config = host_config | 
| 854 | 876 | 
| 855 def __call__(self, signum, _stackframe): | 877 def __call__(self, signum, _stackframe): | 
| (...skipping 518 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1374 else: | 1396 else: | 
| 1375 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) | 1397 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) | 
| 1376 elif os.WIFSIGNALED(status): | 1398 elif os.WIFSIGNALED(status): | 
| 1377 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) | 1399 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) | 
| 1378 | 1400 | 
| 1379 | 1401 | 
| 1380 if __name__ == "__main__": | 1402 if __name__ == "__main__": | 
| 1381 logging.basicConfig(level=logging.DEBUG, | 1403 logging.basicConfig(level=logging.DEBUG, | 
| 1382 format="%(asctime)s:%(levelname)s:%(message)s") | 1404 format="%(asctime)s:%(levelname)s:%(message)s") | 
| 1383 sys.exit(main()) | 1405 sys.exit(main()) | 
| OLD | NEW |