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