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

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: Reviewer feedback. 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:
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
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
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
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())
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