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

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: Remove timeout. 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
719 # 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.
721 def sigalrm_handler(signum, frame):
722 _ = signum, frame
723 print("No response from daemon. It may have crashed, or may still be "
724 "running in the background.", file=sys.stderr)
725
726 signal.signal(signal.SIGALRM, sigalrm_handler)
727 signal.alarm(30)
Jamie 2016/02/12 18:32:12 Sergey, do you agree that this change makes sense?
Sergey Ulanov 2016/02/12 22:42:25 This timeout may still be useful when the host pro
Jamie 2016/02/12 23:03:04 I've reinstated it but increased it to 120s to mat
728
729 self._write_file.close() 731 self._write_file.close()
730 732
731 # Print lines as they're logged to the pipe until EOF is reached or readline 733 # 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. 734 # is interrupted by one of the signal handlers above.
735 host_ready = False
733 try: 736 try:
734 for line in iter(self._read_file.readline, ''): 737 for line in iter(self._read_file.readline, ''):
735 sys.stderr.write(line) 738 if line[:4] == "MSG:":
739 sys.stderr.write(line[4:])
740 elif line == "READY\n":
741 host_ready = True
742 else:
743 sys.stderr.write("Unrecognized command: " + line)
736 except IOError as e: 744 except IOError as e:
737 if e.errno != errno.EINTR: 745 if e.errno != errno.EINTR:
738 raise 746 raise
739 print("Log file: %s" % os.environ[LOG_FILE_ENV_VAR], file=sys.stderr) 747 print("Log file: %s" % os.environ[LOG_FILE_ENV_VAR], file=sys.stderr)
748 return host_ready
740 749
741 @staticmethod 750 @staticmethod
742 def instance(): 751 def instance():
743 """Returns the singleton instance, if it exists.""" 752 """Returns the singleton instance, if it exists."""
744 return ParentProcessLogger.__instance 753 return ParentProcessLogger.__instance
745 754
746 755
747 def daemonize(): 756 def daemonize():
748 """Background this process and detach from controlling terminal, redirecting 757 """Background this process and detach from controlling terminal, redirecting
749 stdout/stderr to a log file.""" 758 stdout/stderr to a log file."""
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
788 pid = os.fork() 797 pid = os.fork()
789 798
790 if pid == 0: 799 if pid == 0:
791 # Grandchild process 800 # Grandchild process
792 pass 801 pass
793 else: 802 else:
794 # Child process 803 # Child process
795 os._exit(0) # pylint: disable=W0212 804 os._exit(0) # pylint: disable=W0212
796 else: 805 else:
797 # Parent process 806 # Parent process
798 parent_logger.wait_for_logs() 807 if parent_logger.wait_for_logs():
799 os._exit(0) # pylint: disable=W0212 808 os._exit(0) # pylint: disable=W0212
809 else:
810 os._exit(1) # pylint: disable=W0212
800 811
801 logging.info("Daemon process started in the background, logging to '%s'" % 812 logging.info("Daemon process started in the background, logging to '%s'" %
802 os.environ[LOG_FILE_ENV_VAR]) 813 os.environ[LOG_FILE_ENV_VAR])
803 814
804 os.chdir(HOME_DIR) 815 os.chdir(HOME_DIR)
805 816
806 parent_logger.start_logging() 817 parent_logger.start_logging()
807 818
808 # Copy the file-descriptors to create new stdin, stdout and stderr. Note 819 # 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 820 # 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. 846 # process refuses to die for some reason.
836 psutil_proc.wait(timeout=10) 847 psutil_proc.wait(timeout=10)
837 except psutil.TimeoutExpired: 848 except psutil.TimeoutExpired:
838 logging.error("Timed out - sending SIGKILL") 849 logging.error("Timed out - sending SIGKILL")
839 psutil_proc.kill() 850 psutil_proc.kill()
840 except psutil.Error: 851 except psutil.Error:
841 logging.error("Error terminating process") 852 logging.error("Error terminating process")
842 853
843 g_desktops = [] 854 g_desktops = []
844 if ParentProcessLogger.instance(): 855 if ParentProcessLogger.instance():
845 ParentProcessLogger.instance().release_parent() 856 ParentProcessLogger.instance().release_parent(False)
846 857
847 class SignalHandler: 858 class SignalHandler:
848 """Reload the config file on SIGHUP. Since we pass the configuration to the 859 """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 860 host processes via stdin, they can't reload it, so terminate them. They will
850 be relaunched automatically with the new config.""" 861 be relaunched automatically with the new config."""
851 862
852 def __init__(self, host_config): 863 def __init__(self, host_config):
853 self.host_config = host_config 864 self.host_config = host_config
854 865
855 def __call__(self, signum, _stackframe): 866 def __call__(self, signum, _stackframe):
(...skipping 518 matching lines...) Expand 10 before | Expand all | Expand 10 after
1374 else: 1385 else:
1375 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) 1386 logging.info("Host exited with status %s." % os.WEXITSTATUS(status))
1376 elif os.WIFSIGNALED(status): 1387 elif os.WIFSIGNALED(status):
1377 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) 1388 logging.info("Host terminated by signal %s." % os.WTERMSIG(status))
1378 1389
1379 1390
1380 if __name__ == "__main__": 1391 if __name__ == "__main__":
1381 logging.basicConfig(level=logging.DEBUG, 1392 logging.basicConfig(level=logging.DEBUG,
1382 format="%(asctime)s:%(levelname)s:%(message)s") 1393 format="%(asctime)s:%(levelname)s:%(message)s")
1383 sys.exit(main()) 1394 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