| 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 |