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

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: 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
707 if success:
708 self._write_file.write("READY\n")
709 self._write_file.flush()
701 if not self._write_file.closed: 710 if not self._write_file.closed:
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.
Lambros 2016/02/10 19:43:32 Document the return value (maybe in a "Returns:" s
Jamie 2016/02/11 00:00:22 Done.
708 """ 717 """
709 # If Ctrl-C is pressed, inform the user that the daemon is still running. 718 # 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. 719 # This signal will cause the read loop below to stop with an EINTR IOError.
711 def sigint_handler(signum, frame): 720 def sigint_handler(signum, frame):
712 _ = signum, frame 721 _ = signum, frame
713 print("Interrupted. The daemon is still running in the background.", 722 print("Interrupted. The daemon is still running in the background.",
714 file=sys.stderr) 723 file=sys.stderr)
715 724
716 signal.signal(signal.SIGINT, sigint_handler) 725 signal.signal(signal.SIGINT, sigint_handler)
717 726
718 # Install a fallback timeout to release the parent process, in case the 727 # Install a fallback timeout to release the parent process, in case the
719 # daemon never responds (e.g. host crash-looping, daemon killed). 728 # 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. 729 # This signal will cause the read loop below to stop with an EINTR IOError.
721 def sigalrm_handler(signum, frame): 730 def sigalrm_handler(signum, frame):
722 _ = signum, frame 731 _ = signum, frame
723 print("No response from daemon. It may have crashed, or may still be " 732 print("No response from daemon. It may have crashed, or may still be "
724 "running in the background.", file=sys.stderr) 733 "running in the background.", file=sys.stderr)
725 734
726 signal.signal(signal.SIGALRM, sigalrm_handler) 735 signal.signal(signal.SIGALRM, sigalrm_handler)
727 signal.alarm(30) 736 signal.alarm(30)
728 737
729 self._write_file.close() 738 self._write_file.close()
730 739
731 # Print lines as they're logged to the pipe until EOF is reached or readline 740 # 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. 741 # is interrupted by one of the signal handlers above.
742 host_ready = False
733 try: 743 try:
734 for line in iter(self._read_file.readline, ''): 744 for line in iter(self._read_file.readline, ''):
735 sys.stderr.write(line) 745 if line[:4] == "MSG:":
746 sys.stderr.write(line[4:])
747 elif line == "READY\n":
748 host_ready = True
749 else:
750 sys.stderr.write("Unrecognized command: " + line)
736 except IOError as e: 751 except IOError as e:
737 if e.errno != errno.EINTR: 752 if e.errno != errno.EINTR:
738 raise 753 raise
739 print("Log file: %s" % os.environ[LOG_FILE_ENV_VAR], file=sys.stderr) 754 print("Log file: %s" % os.environ[LOG_FILE_ENV_VAR], file=sys.stderr)
755 return host_ready
740 756
741 @staticmethod 757 @staticmethod
742 def instance(): 758 def instance():
743 """Returns the singleton instance, if it exists.""" 759 """Returns the singleton instance, if it exists."""
744 return ParentProcessLogger.__instance 760 return ParentProcessLogger.__instance
745 761
746 762
747 def daemonize(): 763 def daemonize():
748 """Background this process and detach from controlling terminal, redirecting 764 """Background this process and detach from controlling terminal, redirecting
749 stdout/stderr to a log file.""" 765 stdout/stderr to a log file."""
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
788 pid = os.fork() 804 pid = os.fork()
789 805
790 if pid == 0: 806 if pid == 0:
791 # Grandchild process 807 # Grandchild process
792 pass 808 pass
793 else: 809 else:
794 # Child process 810 # Child process
795 os._exit(0) # pylint: disable=W0212 811 os._exit(0) # pylint: disable=W0212
796 else: 812 else:
797 # Parent process 813 # Parent process
798 parent_logger.wait_for_logs() 814 if parent_logger.wait_for_logs():
799 os._exit(0) # pylint: disable=W0212 815 os._exit(0) # pylint: disable=W0212
816 else:
817 os._exit(1) # pylint: disable=W0212
Lambros 2016/02/10 19:43:32 What will happen in the case of timeout after 30 s
Jamie 2016/02/11 00:00:22 According to the comment on line 733, the read loo
Lambros 2016/02/11 21:46:02 My reading of the old code is that, after the 30s
Jamie 2016/02/12 18:32:11 Good point. I've removed the timeout from this scr
800 818
801 logging.info("Daemon process started in the background, logging to '%s'" % 819 logging.info("Daemon process started in the background, logging to '%s'" %
802 os.environ[LOG_FILE_ENV_VAR]) 820 os.environ[LOG_FILE_ENV_VAR])
803 821
804 os.chdir(HOME_DIR) 822 os.chdir(HOME_DIR)
805 823
806 parent_logger.start_logging() 824 parent_logger.start_logging()
807 825
808 # Copy the file-descriptors to create new stdin, stdout and stderr. Note 826 # 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 827 # 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. 853 # process refuses to die for some reason.
836 psutil_proc.wait(timeout=10) 854 psutil_proc.wait(timeout=10)
837 except psutil.TimeoutExpired: 855 except psutil.TimeoutExpired:
838 logging.error("Timed out - sending SIGKILL") 856 logging.error("Timed out - sending SIGKILL")
839 psutil_proc.kill() 857 psutil_proc.kill()
840 except psutil.Error: 858 except psutil.Error:
841 logging.error("Error terminating process") 859 logging.error("Error terminating process")
842 860
843 g_desktops = [] 861 g_desktops = []
844 if ParentProcessLogger.instance(): 862 if ParentProcessLogger.instance():
845 ParentProcessLogger.instance().release_parent() 863 ParentProcessLogger.instance().release_parent(False)
846 864
847 class SignalHandler: 865 class SignalHandler:
848 """Reload the config file on SIGHUP. Since we pass the configuration to the 866 """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 867 host processes via stdin, they can't reload it, so terminate them. They will
850 be relaunched automatically with the new config.""" 868 be relaunched automatically with the new config."""
851 869
852 def __init__(self, host_config): 870 def __init__(self, host_config):
853 self.host_config = host_config 871 self.host_config = host_config
854 872
855 def __call__(self, signum, _stackframe): 873 def __call__(self, signum, _stackframe):
(...skipping 518 matching lines...) Expand 10 before | Expand all | Expand 10 after
1374 else: 1392 else:
1375 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) 1393 logging.info("Host exited with status %s." % os.WEXITSTATUS(status))
1376 elif os.WIFSIGNALED(status): 1394 elif os.WIFSIGNALED(status):
1377 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) 1395 logging.info("Host terminated by signal %s." % os.WTERMSIG(status))
1378 1396
1379 1397
1380 if __name__ == "__main__": 1398 if __name__ == "__main__":
1381 logging.basicConfig(level=logging.DEBUG, 1399 logging.basicConfig(level=logging.DEBUG,
1382 format="%(asctime)s:%(levelname)s:%(message)s") 1400 format="%(asctime)s:%(levelname)s:%(message)s")
1383 sys.exit(main()) 1401 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