OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // On Linux, when the user tries to launch a second copy of chrome, we check | 5 // On Linux, when the user tries to launch a second copy of chrome, we check |
6 // for a socket in the user's profile directory. If the socket file is open we | 6 // for a socket in the user's profile directory. If the socket file is open we |
7 // send a message to the first chrome browser process with the current | 7 // send a message to the first chrome browser process with the current |
8 // directory and second process command line flags. The second process then | 8 // directory and second process command line flags. The second process then |
9 // exits. | 9 // exits. |
10 // | 10 // |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 using content::BrowserThread; | 104 using content::BrowserThread; |
105 | 105 |
106 namespace { | 106 namespace { |
107 | 107 |
108 // Timeout for the current browser process to respond. 20 seconds should be | 108 // Timeout for the current browser process to respond. 20 seconds should be |
109 // enough. | 109 // enough. |
110 const int kTimeoutInSeconds = 20; | 110 const int kTimeoutInSeconds = 20; |
111 // Number of retries to notify the browser. 20 retries over 20 seconds = 1 try | 111 // Number of retries to notify the browser. 20 retries over 20 seconds = 1 try |
112 // per second. | 112 // per second. |
113 const int kRetryAttempts = 20; | 113 const int kRetryAttempts = 20; |
114 static bool g_disable_prompt; | |
115 const char kStartToken[] = "START"; | 114 const char kStartToken[] = "START"; |
116 const char kACKToken[] = "ACK"; | 115 const char kACKToken[] = "ACK"; |
117 const char kShutdownToken[] = "SHUTDOWN"; | 116 const char kShutdownToken[] = "SHUTDOWN"; |
118 const char kTokenDelimiter = '\0'; | 117 const char kTokenDelimiter = '\0'; |
119 const int kMaxMessageLength = 32 * 1024; | 118 const int kMaxMessageLength = 32 * 1024; |
120 const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1; | 119 const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1; |
121 | 120 |
122 const char kLockDelimiter = '-'; | 121 const char kLockDelimiter = '-'; |
123 | 122 |
| 123 bool g_disable_prompt = false; |
| 124 bool g_skip_is_chrome_process_check = false; |
| 125 |
124 // Set the close-on-exec bit on a file descriptor. | 126 // Set the close-on-exec bit on a file descriptor. |
125 // Returns 0 on success, -1 on failure. | 127 // Returns 0 on success, -1 on failure. |
126 int SetCloseOnExec(int fd) { | 128 int SetCloseOnExec(int fd) { |
127 int flags = fcntl(fd, F_GETFD, 0); | 129 int flags = fcntl(fd, F_GETFD, 0); |
128 if (-1 == flags) | 130 if (-1 == flags) |
129 return flags; | 131 return flags; |
130 if (flags & FD_CLOEXEC) | 132 if (flags & FD_CLOEXEC) |
131 return 0; | 133 return 0; |
132 return fcntl(fd, F_SETFD, flags | FD_CLOEXEC); | 134 return fcntl(fd, F_SETFD, flags | FD_CLOEXEC); |
133 } | 135 } |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 return true; | 333 return true; |
332 #endif | 334 #endif |
333 | 335 |
334 NOTREACHED(); | 336 NOTREACHED(); |
335 return false; | 337 return false; |
336 } | 338 } |
337 | 339 |
338 bool IsChromeProcess(pid_t pid) { | 340 bool IsChromeProcess(pid_t pid) { |
339 base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid)); | 341 base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid)); |
340 return (!other_chrome_path.empty() && | 342 return (!other_chrome_path.empty() && |
341 other_chrome_path.BaseName() == | 343 (g_skip_is_chrome_process_check || |
342 base::FilePath(chrome::kBrowserProcessExecutableName)); | 344 other_chrome_path.BaseName() == |
| 345 base::FilePath(chrome::kBrowserProcessExecutableName))); |
343 } | 346 } |
344 | 347 |
345 // A helper class to hold onto a socket. | 348 // A helper class to hold onto a socket. |
346 class ScopedSocket { | 349 class ScopedSocket { |
347 public: | 350 public: |
348 ScopedSocket() : fd_(-1) { Reset(); } | 351 ScopedSocket() : fd_(-1) { Reset(); } |
349 ~ScopedSocket() { Close(); } | 352 ~ScopedSocket() { Close(); } |
350 int fd() { return fd_; } | 353 int fd() { return fd_; } |
351 void Reset() { | 354 void Reset() { |
352 Close(); | 355 Close(); |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
457 // version has run. | 460 // version has run. |
458 if (!base::DeleteFile(lock_path, false)) { | 461 if (!base::DeleteFile(lock_path, false)) { |
459 PLOG(ERROR) << "Could not delete old singleton lock."; | 462 PLOG(ERROR) << "Could not delete old singleton lock."; |
460 return false; | 463 return false; |
461 } | 464 } |
462 | 465 |
463 return SymlinkPath(symlink_content, lock_path); | 466 return SymlinkPath(symlink_content, lock_path); |
464 } | 467 } |
465 #endif // defined(OS_MACOSX) | 468 #endif // defined(OS_MACOSX) |
466 | 469 |
| 470 void SendRemoteProcessInteractionResultHistogram( |
| 471 ProcessSingleton::RemoteProcessInteractionResult result) { |
| 472 UMA_HISTOGRAM_ENUMERATION( |
| 473 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", result, |
| 474 ProcessSingleton::REMOTE_PROCESS_INTERACTION_RESULT_COUNT); |
| 475 } |
| 476 |
| 477 void SendRemoteHungProcessTerminateReasonHistogram( |
| 478 ProcessSingleton::RemoteHungProcessTerminateReason reason) { |
| 479 UMA_HISTOGRAM_ENUMERATION( |
| 480 "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason", reason, |
| 481 ProcessSingleton::REMOTE_HUNG_PROCESS_TERMINATE_REASON_COUNT); |
| 482 } |
| 483 |
467 } // namespace | 484 } // namespace |
468 | 485 |
469 /////////////////////////////////////////////////////////////////////////////// | 486 /////////////////////////////////////////////////////////////////////////////// |
470 // ProcessSingleton::LinuxWatcher | 487 // ProcessSingleton::LinuxWatcher |
471 // A helper class for a Linux specific implementation of the process singleton. | 488 // A helper class for a Linux specific implementation of the process singleton. |
472 // This class sets up a listener on the singleton socket and handles parsing | 489 // This class sets up a listener on the singleton socket and handles parsing |
473 // messages that come in on the singleton socket. | 490 // messages that come in on the singleton socket. |
474 class ProcessSingleton::LinuxWatcher | 491 class ProcessSingleton::LinuxWatcher |
475 : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher, | 492 : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher, |
476 BrowserThread::DeleteOnIOThread> { | 493 BrowserThread::DeleteOnIOThread> { |
(...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
769 std::string hostname; | 786 std::string hostname; |
770 int pid; | 787 int pid; |
771 if (!ParseLockPath(lock_path_, &hostname, &pid)) { | 788 if (!ParseLockPath(lock_path_, &hostname, &pid)) { |
772 // No lockfile exists. | 789 // No lockfile exists. |
773 return PROCESS_NONE; | 790 return PROCESS_NONE; |
774 } | 791 } |
775 | 792 |
776 if (hostname.empty()) { | 793 if (hostname.empty()) { |
777 // Invalid lockfile. | 794 // Invalid lockfile. |
778 UnlinkPath(lock_path_); | 795 UnlinkPath(lock_path_); |
| 796 SendRemoteProcessInteractionResultHistogram(INVALID_LOCK_FILE); |
779 return PROCESS_NONE; | 797 return PROCESS_NONE; |
780 } | 798 } |
781 | 799 |
782 if (hostname != net::GetHostName() && !IsChromeProcess(pid)) { | 800 if (hostname != net::GetHostName() && !IsChromeProcess(pid)) { |
783 // Locked by process on another host. If the user selected to unlock | 801 // Locked by process on another host. If the user selected to unlock |
784 // the profile, try to continue; otherwise quit. | 802 // the profile, try to continue; otherwise quit. |
785 if (DisplayProfileInUseError(lock_path_, hostname, pid)) { | 803 if (DisplayProfileInUseError(lock_path_, hostname, pid)) { |
786 UnlinkPath(lock_path_); | 804 UnlinkPath(lock_path_); |
| 805 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED); |
787 return PROCESS_NONE; | 806 return PROCESS_NONE; |
788 } | 807 } |
789 return PROFILE_IN_USE; | 808 return PROFILE_IN_USE; |
790 } | 809 } |
791 | 810 |
792 if (!IsChromeProcess(pid)) { | 811 if (!IsChromeProcess(pid)) { |
793 // Orphaned lockfile (no process with pid, or non-chrome process.) | 812 // Orphaned lockfile (no process with pid, or non-chrome process.) |
794 UnlinkPath(lock_path_); | 813 UnlinkPath(lock_path_); |
| 814 SendRemoteProcessInteractionResultHistogram(ORPHANED_LOCK_FILE); |
795 return PROCESS_NONE; | 815 return PROCESS_NONE; |
796 } | 816 } |
797 | 817 |
798 if (IsSameChromeInstance(pid)) { | 818 if (IsSameChromeInstance(pid)) { |
799 // Orphaned lockfile (pid is part of same chrome instance we are, even | 819 // Orphaned lockfile (pid is part of same chrome instance we are, even |
800 // though we haven't tried to create a lockfile yet). | 820 // though we haven't tried to create a lockfile yet). |
801 UnlinkPath(lock_path_); | 821 UnlinkPath(lock_path_); |
| 822 SendRemoteProcessInteractionResultHistogram(SAME_BROWSER_INSTANCE); |
802 return PROCESS_NONE; | 823 return PROCESS_NONE; |
803 } | 824 } |
804 | 825 |
805 if (retries == retry_attempts) { | 826 if (retries == retry_attempts) { |
806 // Retries failed. Kill the unresponsive chrome process and continue. | 827 // Retries failed. Kill the unresponsive chrome process and continue. |
807 if (!kill_unresponsive || !KillProcessByLockPath()) | 828 if (!kill_unresponsive || !KillProcessByLockPath()) |
808 return PROFILE_IN_USE; | 829 return PROFILE_IN_USE; |
| 830 SendRemoteHungProcessTerminateReasonHistogram(NOTIFY_ATTEMPTS_EXCEEDED); |
809 return PROCESS_NONE; | 831 return PROCESS_NONE; |
810 } | 832 } |
811 | 833 |
812 base::PlatformThread::Sleep(sleep_interval); | 834 base::PlatformThread::Sleep(sleep_interval); |
813 } | 835 } |
814 | 836 |
815 timeval socket_timeout = TimeDeltaToTimeVal(timeout); | 837 timeval socket_timeout = TimeDeltaToTimeVal(timeout); |
816 setsockopt(socket.fd(), | 838 setsockopt(socket.fd(), |
817 SOL_SOCKET, | 839 SOL_SOCKET, |
818 SO_SNDTIMEO, | 840 SO_SNDTIMEO, |
(...skipping 15 matching lines...) Expand all Loading... |
834 it != argv.end(); ++it) { | 856 it != argv.end(); ++it) { |
835 to_send.push_back(kTokenDelimiter); | 857 to_send.push_back(kTokenDelimiter); |
836 to_send.append(*it); | 858 to_send.append(*it); |
837 } | 859 } |
838 | 860 |
839 // Send the message | 861 // Send the message |
840 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { | 862 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { |
841 // Try to kill the other process, because it might have been dead. | 863 // Try to kill the other process, because it might have been dead. |
842 if (!kill_unresponsive || !KillProcessByLockPath()) | 864 if (!kill_unresponsive || !KillProcessByLockPath()) |
843 return PROFILE_IN_USE; | 865 return PROFILE_IN_USE; |
| 866 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_WRITE_FAILED); |
844 return PROCESS_NONE; | 867 return PROCESS_NONE; |
845 } | 868 } |
846 | 869 |
847 if (shutdown(socket.fd(), SHUT_WR) < 0) | 870 if (shutdown(socket.fd(), SHUT_WR) < 0) |
848 PLOG(ERROR) << "shutdown() failed"; | 871 PLOG(ERROR) << "shutdown() failed"; |
849 | 872 |
850 // Read ACK message from the other process. It might be blocked for a certain | 873 // Read ACK message from the other process. It might be blocked for a certain |
851 // timeout, to make sure the other process has enough time to return ACK. | 874 // timeout, to make sure the other process has enough time to return ACK. |
852 char buf[kMaxACKMessageLength + 1]; | 875 char buf[kMaxACKMessageLength + 1]; |
853 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); | 876 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); |
854 | 877 |
855 // Failed to read ACK, the other process might have been frozen. | 878 // Failed to read ACK, the other process might have been frozen. |
856 if (len <= 0) { | 879 if (len <= 0) { |
857 if (!kill_unresponsive || !KillProcessByLockPath()) | 880 if (!kill_unresponsive || !KillProcessByLockPath()) |
858 return PROFILE_IN_USE; | 881 return PROFILE_IN_USE; |
| 882 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_READ_FAILED); |
859 return PROCESS_NONE; | 883 return PROCESS_NONE; |
860 } | 884 } |
861 | 885 |
862 buf[len] = '\0'; | 886 buf[len] = '\0'; |
863 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { | 887 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { |
864 // The other process is shutting down, it's safe to start a new process. | 888 // The other process is shutting down, it's safe to start a new process. |
| 889 SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_SHUTTING_DOWN); |
865 return PROCESS_NONE; | 890 return PROCESS_NONE; |
866 } else if (strncmp(buf, kACKToken, arraysize(kACKToken) - 1) == 0) { | 891 } else if (strncmp(buf, kACKToken, arraysize(kACKToken) - 1) == 0) { |
867 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) | 892 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) |
868 // Likely NULL in unit tests. | 893 // Likely NULL in unit tests. |
869 views::LinuxUI* linux_ui = views::LinuxUI::instance(); | 894 views::LinuxUI* linux_ui = views::LinuxUI::instance(); |
870 if (linux_ui) | 895 if (linux_ui) |
871 linux_ui->NotifyWindowManagerStartupComplete(); | 896 linux_ui->NotifyWindowManagerStartupComplete(); |
872 #endif | 897 #endif |
873 | 898 |
874 // Assume the other process is handling the request. | 899 // Assume the other process is handling the request. |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
938 | 963 |
939 void ProcessSingleton::OverrideKillCallbackForTesting( | 964 void ProcessSingleton::OverrideKillCallbackForTesting( |
940 const base::Callback<void(int)>& callback) { | 965 const base::Callback<void(int)>& callback) { |
941 kill_callback_ = callback; | 966 kill_callback_ = callback; |
942 } | 967 } |
943 | 968 |
944 void ProcessSingleton::DisablePromptForTesting() { | 969 void ProcessSingleton::DisablePromptForTesting() { |
945 g_disable_prompt = true; | 970 g_disable_prompt = true; |
946 } | 971 } |
947 | 972 |
| 973 void ProcessSingleton::SkipIsChromeProcessCheckForTesting(bool skip) { |
| 974 g_skip_is_chrome_process_check = skip; |
| 975 } |
| 976 |
948 bool ProcessSingleton::Create() { | 977 bool ProcessSingleton::Create() { |
949 int sock; | 978 int sock; |
950 sockaddr_un addr; | 979 sockaddr_un addr; |
951 | 980 |
952 // The symlink lock is pointed to the hostname and process id, so other | 981 // The symlink lock is pointed to the hostname and process id, so other |
953 // processes can find it out. | 982 // processes can find it out. |
954 base::FilePath symlink_content(base::StringPrintf( | 983 base::FilePath symlink_content(base::StringPrintf( |
955 "%s%c%u", | 984 "%s%c%u", |
956 net::GetHostName().c_str(), | 985 net::GetHostName().c_str(), |
957 kLockDelimiter, | 986 kLockDelimiter, |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1050 } | 1079 } |
1051 return true; | 1080 return true; |
1052 } | 1081 } |
1053 | 1082 |
1054 bool ProcessSingleton::KillProcessByLockPath() { | 1083 bool ProcessSingleton::KillProcessByLockPath() { |
1055 std::string hostname; | 1084 std::string hostname; |
1056 int pid; | 1085 int pid; |
1057 ParseLockPath(lock_path_, &hostname, &pid); | 1086 ParseLockPath(lock_path_, &hostname, &pid); |
1058 | 1087 |
1059 if (!hostname.empty() && hostname != net::GetHostName()) { | 1088 if (!hostname.empty() && hostname != net::GetHostName()) { |
1060 return DisplayProfileInUseError(lock_path_, hostname, pid); | 1089 bool res = DisplayProfileInUseError(lock_path_, hostname, pid); |
| 1090 if (res) |
| 1091 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED_BEFORE_KILL); |
| 1092 return res; |
1061 } | 1093 } |
1062 UnlinkPath(lock_path_); | 1094 UnlinkPath(lock_path_); |
1063 | 1095 |
1064 if (IsSameChromeInstance(pid)) | 1096 if (IsSameChromeInstance(pid)) { |
| 1097 SendRemoteProcessInteractionResultHistogram( |
| 1098 SAME_BROWSER_INSTANCE_BEFORE_KILL); |
1065 return true; | 1099 return true; |
| 1100 } |
1066 | 1101 |
1067 if (pid > 0) { | 1102 if (pid > 0) { |
1068 kill_callback_.Run(pid); | 1103 kill_callback_.Run(pid); |
1069 return true; | 1104 return true; |
1070 } | 1105 } |
1071 | 1106 |
| 1107 SendRemoteProcessInteractionResultHistogram(FAILED_TO_EXTRACT_PID); |
| 1108 |
1072 LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value(); | 1109 LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value(); |
1073 return true; | 1110 return true; |
1074 } | 1111 } |
1075 | 1112 |
1076 void ProcessSingleton::KillProcess(int pid) { | 1113 void ProcessSingleton::KillProcess(int pid) { |
1077 // TODO(james.su@gmail.com): Is SIGKILL ok? | 1114 // TODO(james.su@gmail.com): Is SIGKILL ok? |
1078 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); | 1115 int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); |
1079 // ESRCH = No Such Process (can happen if the other process is already in | 1116 // ESRCH = No Such Process (can happen if the other process is already in |
1080 // progress of shutting down and finishes before we try to kill it). | 1117 // progress of shutting down and finishes before we try to kill it). |
1081 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " | 1118 DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " |
1082 << base::safe_strerror(errno); | 1119 << base::safe_strerror(errno); |
| 1120 |
| 1121 int error_code = (rv == 0) ? 0 : errno; |
| 1122 UMA_HISTOGRAM_SPARSE_SLOWLY( |
| 1123 "Chrome.ProcessSingleton.TerminateProcessErrorCode.Posix", error_code); |
| 1124 |
| 1125 RemoteProcessInteractionResult action = TERMINATE_SUCCEEDED; |
| 1126 if (rv != 0) { |
| 1127 switch (error_code) { |
| 1128 case ESRCH: |
| 1129 action = REMOTE_PROCESS_NOT_FOUND; |
| 1130 break; |
| 1131 case EPERM: |
| 1132 action = TERMINATE_NOT_ENOUGH_PERMISSIONS; |
| 1133 break; |
| 1134 default: |
| 1135 action = TERMINATE_FAILED; |
| 1136 break; |
| 1137 } |
| 1138 } |
| 1139 SendRemoteProcessInteractionResultHistogram(action); |
1083 } | 1140 } |
OLD | NEW |