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 |