| 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 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 115 const char kACKToken[] = "ACK"; | 115 const char kACKToken[] = "ACK"; |
| 116 const char kShutdownToken[] = "SHUTDOWN"; | 116 const char kShutdownToken[] = "SHUTDOWN"; |
| 117 const char kTokenDelimiter = '\0'; | 117 const char kTokenDelimiter = '\0'; |
| 118 const int kMaxMessageLength = 32 * 1024; | 118 const int kMaxMessageLength = 32 * 1024; |
| 119 const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1; | 119 const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1; |
| 120 | 120 |
| 121 const char kLockDelimiter = '-'; | 121 const char kLockDelimiter = '-'; |
| 122 | 122 |
| 123 bool g_disable_prompt = false; | 123 bool g_disable_prompt = false; |
| 124 bool g_skip_is_chrome_process_check = false; | 124 bool g_skip_is_chrome_process_check = false; |
| 125 bool g_user_opted_unlock_in_use_profile = false; |
| 125 | 126 |
| 126 // Set the close-on-exec bit on a file descriptor. | 127 // Set the close-on-exec bit on a file descriptor. |
| 127 // Returns 0 on success, -1 on failure. | 128 // Returns 0 on success, -1 on failure. |
| 128 int SetCloseOnExec(int fd) { | 129 int SetCloseOnExec(int fd) { |
| 129 int flags = fcntl(fd, F_GETFD, 0); | 130 int flags = fcntl(fd, F_GETFD, 0); |
| 130 if (-1 == flags) | 131 if (-1 == flags) |
| 131 return flags; | 132 return flags; |
| 132 if (flags & FD_CLOEXEC) | 133 if (flags & FD_CLOEXEC) |
| 133 return 0; | 134 return 0; |
| 134 return fcntl(fd, F_SETFD, flags | FD_CLOEXEC); | 135 return fcntl(fd, F_SETFD, flags | FD_CLOEXEC); |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 bool DisplayProfileInUseError(const base::FilePath& lock_path, | 316 bool DisplayProfileInUseError(const base::FilePath& lock_path, |
| 316 const std::string& hostname, | 317 const std::string& hostname, |
| 317 int pid) { | 318 int pid) { |
| 318 base::string16 error = l10n_util::GetStringFUTF16( | 319 base::string16 error = l10n_util::GetStringFUTF16( |
| 319 IDS_PROFILE_IN_USE_POSIX, | 320 IDS_PROFILE_IN_USE_POSIX, |
| 320 base::IntToString16(pid), | 321 base::IntToString16(pid), |
| 321 base::ASCIIToUTF16(hostname)); | 322 base::ASCIIToUTF16(hostname)); |
| 322 LOG(ERROR) << error; | 323 LOG(ERROR) << error; |
| 323 | 324 |
| 324 if (g_disable_prompt) | 325 if (g_disable_prompt) |
| 325 return false; | 326 return g_user_opted_unlock_in_use_profile; |
| 326 | 327 |
| 327 #if defined(OS_LINUX) | 328 #if defined(OS_LINUX) |
| 328 base::string16 relaunch_button_text = l10n_util::GetStringUTF16( | 329 base::string16 relaunch_button_text = l10n_util::GetStringUTF16( |
| 329 IDS_PROFILE_IN_USE_LINUX_RELAUNCH); | 330 IDS_PROFILE_IN_USE_LINUX_RELAUNCH); |
| 330 return ShowProcessSingletonDialog(error, relaunch_button_text); | 331 return ShowProcessSingletonDialog(error, relaunch_button_text); |
| 331 #elif defined(OS_MACOSX) | 332 #elif defined(OS_MACOSX) |
| 332 // On Mac, always usurp the lock. | 333 // On Mac, always usurp the lock. |
| 333 return true; | 334 return true; |
| 334 #endif | 335 #endif |
| 335 | 336 |
| 336 NOTREACHED(); | 337 NOTREACHED(); |
| 337 return false; | 338 return false; |
| 338 } | 339 } |
| 339 | 340 |
| 340 bool IsChromeProcess(pid_t pid) { | 341 bool IsChromeProcess(pid_t pid) { |
| 342 if (g_skip_is_chrome_process_check) |
| 343 return true; |
| 344 |
| 341 base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid)); | 345 base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid)); |
| 342 return (!other_chrome_path.empty() && | 346 return (!other_chrome_path.empty() && |
| 343 (g_skip_is_chrome_process_check || | 347 other_chrome_path.BaseName() == |
| 344 other_chrome_path.BaseName() == | 348 base::FilePath(chrome::kBrowserProcessExecutableName)); |
| 345 base::FilePath(chrome::kBrowserProcessExecutableName))); | |
| 346 } | 349 } |
| 347 | 350 |
| 348 // A helper class to hold onto a socket. | 351 // A helper class to hold onto a socket. |
| 349 class ScopedSocket { | 352 class ScopedSocket { |
| 350 public: | 353 public: |
| 351 ScopedSocket() : fd_(-1) { Reset(); } | 354 ScopedSocket() : fd_(-1) { Reset(); } |
| 352 ~ScopedSocket() { Close(); } | 355 ~ScopedSocket() { Close(); } |
| 353 int fd() { return fd_; } | 356 int fd() { return fd_; } |
| 354 void Reset() { | 357 void Reset() { |
| 355 Close(); | 358 Close(); |
| (...skipping 462 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 818 if (IsSameChromeInstance(pid)) { | 821 if (IsSameChromeInstance(pid)) { |
| 819 // Orphaned lockfile (pid is part of same chrome instance we are, even | 822 // Orphaned lockfile (pid is part of same chrome instance we are, even |
| 820 // though we haven't tried to create a lockfile yet). | 823 // though we haven't tried to create a lockfile yet). |
| 821 UnlinkPath(lock_path_); | 824 UnlinkPath(lock_path_); |
| 822 SendRemoteProcessInteractionResultHistogram(SAME_BROWSER_INSTANCE); | 825 SendRemoteProcessInteractionResultHistogram(SAME_BROWSER_INSTANCE); |
| 823 return PROCESS_NONE; | 826 return PROCESS_NONE; |
| 824 } | 827 } |
| 825 | 828 |
| 826 if (retries == retry_attempts) { | 829 if (retries == retry_attempts) { |
| 827 // Retries failed. Kill the unresponsive chrome process and continue. | 830 // Retries failed. Kill the unresponsive chrome process and continue. |
| 828 if (!kill_unresponsive || !KillProcessByLockPath()) | 831 if (!kill_unresponsive || !KillProcessByLockPath(false)) |
| 829 return PROFILE_IN_USE; | 832 return PROFILE_IN_USE; |
| 830 SendRemoteHungProcessTerminateReasonHistogram(NOTIFY_ATTEMPTS_EXCEEDED); | 833 SendRemoteHungProcessTerminateReasonHistogram(NOTIFY_ATTEMPTS_EXCEEDED); |
| 831 return PROCESS_NONE; | 834 return PROCESS_NONE; |
| 832 } | 835 } |
| 833 | 836 |
| 834 base::PlatformThread::Sleep(sleep_interval); | 837 base::PlatformThread::Sleep(sleep_interval); |
| 835 } | 838 } |
| 836 | 839 |
| 837 timeval socket_timeout = TimeDeltaToTimeVal(timeout); | 840 timeval socket_timeout = TimeDeltaToTimeVal(timeout); |
| 838 setsockopt(socket.fd(), | 841 setsockopt(socket.fd(), |
| (...skipping 15 matching lines...) Expand all Loading... |
| 854 const std::vector<std::string>& argv = cmd_line.argv(); | 857 const std::vector<std::string>& argv = cmd_line.argv(); |
| 855 for (std::vector<std::string>::const_iterator it = argv.begin(); | 858 for (std::vector<std::string>::const_iterator it = argv.begin(); |
| 856 it != argv.end(); ++it) { | 859 it != argv.end(); ++it) { |
| 857 to_send.push_back(kTokenDelimiter); | 860 to_send.push_back(kTokenDelimiter); |
| 858 to_send.append(*it); | 861 to_send.append(*it); |
| 859 } | 862 } |
| 860 | 863 |
| 861 // Send the message | 864 // Send the message |
| 862 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { | 865 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { |
| 863 // Try to kill the other process, because it might have been dead. | 866 // Try to kill the other process, because it might have been dead. |
| 864 if (!kill_unresponsive || !KillProcessByLockPath()) | 867 if (!kill_unresponsive || !KillProcessByLockPath(true)) |
| 865 return PROFILE_IN_USE; | 868 return PROFILE_IN_USE; |
| 866 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_WRITE_FAILED); | 869 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_WRITE_FAILED); |
| 867 return PROCESS_NONE; | 870 return PROCESS_NONE; |
| 868 } | 871 } |
| 869 | 872 |
| 870 if (shutdown(socket.fd(), SHUT_WR) < 0) | 873 if (shutdown(socket.fd(), SHUT_WR) < 0) |
| 871 PLOG(ERROR) << "shutdown() failed"; | 874 PLOG(ERROR) << "shutdown() failed"; |
| 872 | 875 |
| 873 // Read ACK message from the other process. It might be blocked for a certain | 876 // Read ACK message from the other process. It might be blocked for a certain |
| 874 // timeout, to make sure the other process has enough time to return ACK. | 877 // timeout, to make sure the other process has enough time to return ACK. |
| 875 char buf[kMaxACKMessageLength + 1]; | 878 char buf[kMaxACKMessageLength + 1]; |
| 876 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); | 879 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); |
| 877 | 880 |
| 878 // Failed to read ACK, the other process might have been frozen. | 881 // Failed to read ACK, the other process might have been frozen. |
| 879 if (len <= 0) { | 882 if (len <= 0) { |
| 880 if (!kill_unresponsive || !KillProcessByLockPath()) | 883 if (!kill_unresponsive || !KillProcessByLockPath(true)) |
| 881 return PROFILE_IN_USE; | 884 return PROFILE_IN_USE; |
| 882 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_READ_FAILED); | 885 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_READ_FAILED); |
| 883 return PROCESS_NONE; | 886 return PROCESS_NONE; |
| 884 } | 887 } |
| 885 | 888 |
| 886 buf[len] = '\0'; | 889 buf[len] = '\0'; |
| 887 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { | 890 if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) { |
| 888 // The other process is shutting down, it's safe to start a new process. | 891 // The other process is shutting down, it's safe to start a new process. |
| 889 SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_SHUTTING_DOWN); | 892 SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_SHUTTING_DOWN); |
| 890 return PROCESS_NONE; | 893 return PROCESS_NONE; |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 959 | 962 |
| 960 void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) { | 963 void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) { |
| 961 current_pid_ = pid; | 964 current_pid_ = pid; |
| 962 } | 965 } |
| 963 | 966 |
| 964 void ProcessSingleton::OverrideKillCallbackForTesting( | 967 void ProcessSingleton::OverrideKillCallbackForTesting( |
| 965 const base::Callback<void(int)>& callback) { | 968 const base::Callback<void(int)>& callback) { |
| 966 kill_callback_ = callback; | 969 kill_callback_ = callback; |
| 967 } | 970 } |
| 968 | 971 |
| 972 // static |
| 969 void ProcessSingleton::DisablePromptForTesting() { | 973 void ProcessSingleton::DisablePromptForTesting() { |
| 970 g_disable_prompt = true; | 974 g_disable_prompt = true; |
| 971 } | 975 } |
| 972 | 976 |
| 977 // static |
| 973 void ProcessSingleton::SkipIsChromeProcessCheckForTesting(bool skip) { | 978 void ProcessSingleton::SkipIsChromeProcessCheckForTesting(bool skip) { |
| 974 g_skip_is_chrome_process_check = skip; | 979 g_skip_is_chrome_process_check = skip; |
| 975 } | 980 } |
| 976 | 981 |
| 982 // static |
| 983 void ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting( |
| 984 bool set_unlock) { |
| 985 g_user_opted_unlock_in_use_profile = set_unlock; |
| 986 } |
| 987 |
| 977 bool ProcessSingleton::Create() { | 988 bool ProcessSingleton::Create() { |
| 978 int sock; | 989 int sock; |
| 979 sockaddr_un addr; | 990 sockaddr_un addr; |
| 980 | 991 |
| 981 // The symlink lock is pointed to the hostname and process id, so other | 992 // The symlink lock is pointed to the hostname and process id, so other |
| 982 // processes can find it out. | 993 // processes can find it out. |
| 983 base::FilePath symlink_content(base::StringPrintf( | 994 base::FilePath symlink_content(base::StringPrintf( |
| 984 "%s%c%u", | 995 "%s%c%u", |
| 985 net::GetHostName().c_str(), | 996 net::GetHostName().c_str(), |
| 986 kLockDelimiter, | 997 kLockDelimiter, |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1073 while (pid != cur_pid) { | 1084 while (pid != cur_pid) { |
| 1074 pid = base::GetParentProcessId(pid); | 1085 pid = base::GetParentProcessId(pid); |
| 1075 if (pid < 0) | 1086 if (pid < 0) |
| 1076 return false; | 1087 return false; |
| 1077 if (!IsChromeProcess(pid)) | 1088 if (!IsChromeProcess(pid)) |
| 1078 return false; | 1089 return false; |
| 1079 } | 1090 } |
| 1080 return true; | 1091 return true; |
| 1081 } | 1092 } |
| 1082 | 1093 |
| 1083 bool ProcessSingleton::KillProcessByLockPath() { | 1094 bool ProcessSingleton::KillProcessByLockPath(bool is_connected_to_socket) { |
| 1084 std::string hostname; | 1095 std::string hostname; |
| 1085 int pid; | 1096 int pid; |
| 1086 ParseLockPath(lock_path_, &hostname, &pid); | 1097 ParseLockPath(lock_path_, &hostname, &pid); |
| 1087 | 1098 |
| 1088 if (!hostname.empty() && hostname != net::GetHostName()) { | 1099 if (!hostname.empty() && hostname != net::GetHostName() && |
| 1100 !is_connected_to_socket) { |
| 1089 bool res = DisplayProfileInUseError(lock_path_, hostname, pid); | 1101 bool res = DisplayProfileInUseError(lock_path_, hostname, pid); |
| 1090 if (res) | 1102 if (res) { |
| 1103 UnlinkPath(lock_path_); |
| 1091 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED_BEFORE_KILL); | 1104 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED_BEFORE_KILL); |
| 1105 } |
| 1092 return res; | 1106 return res; |
| 1093 } | 1107 } |
| 1094 UnlinkPath(lock_path_); | 1108 UnlinkPath(lock_path_); |
| 1095 | 1109 |
| 1096 if (IsSameChromeInstance(pid)) { | 1110 if (IsSameChromeInstance(pid)) { |
| 1097 SendRemoteProcessInteractionResultHistogram( | 1111 SendRemoteProcessInteractionResultHistogram( |
| 1098 SAME_BROWSER_INSTANCE_BEFORE_KILL); | 1112 SAME_BROWSER_INSTANCE_BEFORE_KILL); |
| 1099 return true; | 1113 return true; |
| 1100 } | 1114 } |
| 1101 | 1115 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1131 case EPERM: | 1145 case EPERM: |
| 1132 action = TERMINATE_NOT_ENOUGH_PERMISSIONS; | 1146 action = TERMINATE_NOT_ENOUGH_PERMISSIONS; |
| 1133 break; | 1147 break; |
| 1134 default: | 1148 default: |
| 1135 action = TERMINATE_FAILED; | 1149 action = TERMINATE_FAILED; |
| 1136 break; | 1150 break; |
| 1137 } | 1151 } |
| 1138 } | 1152 } |
| 1139 SendRemoteProcessInteractionResultHistogram(action); | 1153 SendRemoteProcessInteractionResultHistogram(action); |
| 1140 } | 1154 } |
| OLD | NEW |