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 |