Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Side by Side Diff: remoting/host/linux/linux_me2me_host.py

Issue 1685793003: Add message from grandchild to parent to indicate successful host launch. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Reinstate timeout and increase 120s to match heartbeat_sender.cc Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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:
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.
734 #
735 # The value of 120s is chosen to match the heartbeat retry timeout in
736 # hearbeat_sender.cc.
721 def sigalrm_handler(signum, frame): 737 def sigalrm_handler(signum, frame):
722 _ = signum, frame 738 _ = signum, frame
723 print("No response from daemon. It may have crashed, or may still be " 739 print("No response from daemon. It may have crashed, or may still be "
724 "running in the background.", file=sys.stderr) 740 "running in the background.", file=sys.stderr)
725 741
726 signal.signal(signal.SIGALRM, sigalrm_handler) 742 signal.signal(signal.SIGALRM, sigalrm_handler)
727 signal.alarm(30) 743 signal.alarm(120)
728 744
729 self._write_file.close() 745 self._write_file.close()
730 746
731 # Print lines as they're logged to the pipe until EOF is reached or readline 747 # 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. 748 # is interrupted by one of the signal handlers above.
749 host_ready = False
733 try: 750 try:
734 for line in iter(self._read_file.readline, ''): 751 for line in iter(self._read_file.readline, ''):
735 sys.stderr.write(line) 752 if line[:4] == "MSG:":
753 sys.stderr.write(line[4:])
754 elif line == "READY\n":
755 host_ready = True
756 else:
757 sys.stderr.write("Unrecognized command: " + line)
736 except IOError as e: 758 except IOError as e:
737 if e.errno != errno.EINTR: 759 if e.errno != errno.EINTR:
738 raise 760 raise
739 print("Log file: %s" % os.environ[LOG_FILE_ENV_VAR], file=sys.stderr) 761 print("Log file: %s" % os.environ[LOG_FILE_ENV_VAR], file=sys.stderr)
762 return host_ready
740 763
741 @staticmethod 764 @staticmethod
742 def instance(): 765 def instance():
743 """Returns the singleton instance, if it exists.""" 766 """Returns the singleton instance, if it exists."""
744 return ParentProcessLogger.__instance 767 return ParentProcessLogger.__instance
745 768
746 769
747 def daemonize(): 770 def daemonize():
748 """Background this process and detach from controlling terminal, redirecting 771 """Background this process and detach from controlling terminal, redirecting
749 stdout/stderr to a log file.""" 772 stdout/stderr to a log file."""
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
788 pid = os.fork() 811 pid = os.fork()
789 812
790 if pid == 0: 813 if pid == 0:
791 # Grandchild process 814 # Grandchild process
792 pass 815 pass
793 else: 816 else:
794 # Child process 817 # Child process
795 os._exit(0) # pylint: disable=W0212 818 os._exit(0) # pylint: disable=W0212
796 else: 819 else:
797 # Parent process 820 # Parent process
798 parent_logger.wait_for_logs() 821 if parent_logger.wait_for_logs():
799 os._exit(0) # pylint: disable=W0212 822 os._exit(0) # pylint: disable=W0212
823 else:
824 os._exit(1) # pylint: disable=W0212
800 825
801 logging.info("Daemon process started in the background, logging to '%s'" % 826 logging.info("Daemon process started in the background, logging to '%s'" %
802 os.environ[LOG_FILE_ENV_VAR]) 827 os.environ[LOG_FILE_ENV_VAR])
803 828
804 os.chdir(HOME_DIR) 829 os.chdir(HOME_DIR)
805 830
806 parent_logger.start_logging() 831 parent_logger.start_logging()
807 832
808 # Copy the file-descriptors to create new stdin, stdout and stderr. Note 833 # 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 834 # that dup2(oldfd, newfd) closes newfd first, so this will close the current
(...skipping 25 matching lines...) Expand all
835 # process refuses to die for some reason. 860 # process refuses to die for some reason.
836 psutil_proc.wait(timeout=10) 861 psutil_proc.wait(timeout=10)
837 except psutil.TimeoutExpired: 862 except psutil.TimeoutExpired:
838 logging.error("Timed out - sending SIGKILL") 863 logging.error("Timed out - sending SIGKILL")
839 psutil_proc.kill() 864 psutil_proc.kill()
840 except psutil.Error: 865 except psutil.Error:
841 logging.error("Error terminating process") 866 logging.error("Error terminating process")
842 867
843 g_desktops = [] 868 g_desktops = []
844 if ParentProcessLogger.instance(): 869 if ParentProcessLogger.instance():
845 ParentProcessLogger.instance().release_parent() 870 ParentProcessLogger.instance().release_parent(False)
846 871
847 class SignalHandler: 872 class SignalHandler:
848 """Reload the config file on SIGHUP. Since we pass the configuration to the 873 """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 874 host processes via stdin, they can't reload it, so terminate them. They will
850 be relaunched automatically with the new config.""" 875 be relaunched automatically with the new config."""
851 876
852 def __init__(self, host_config): 877 def __init__(self, host_config):
853 self.host_config = host_config 878 self.host_config = host_config
854 879
855 def __call__(self, signum, _stackframe): 880 def __call__(self, signum, _stackframe):
(...skipping 518 matching lines...) Expand 10 before | Expand all | Expand 10 after
1374 else: 1399 else:
1375 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) 1400 logging.info("Host exited with status %s." % os.WEXITSTATUS(status))
1376 elif os.WIFSIGNALED(status): 1401 elif os.WIFSIGNALED(status):
1377 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) 1402 logging.info("Host terminated by signal %s." % os.WTERMSIG(status))
1378 1403
1379 1404
1380 if __name__ == "__main__": 1405 if __name__ == "__main__":
1381 logging.basicConfig(level=logging.DEBUG, 1406 logging.basicConfig(level=logging.DEBUG,
1382 format="%(asctime)s:%(levelname)s:%(message)s") 1407 format="%(asctime)s:%(levelname)s:%(message)s")
1383 sys.exit(main()) 1408 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698