[email protected] | e3ce40a | 2012-01-31 03:03:03 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 7c47ae3e | 2009-02-18 00:34:21 | [diff] [blame] | 5 | #ifndef CHROME_BROWSER_PROCESS_SINGLETON_H_ |
| 6 | #define CHROME_BROWSER_PROCESS_SINGLETON_H_ |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 7 | |
gab | 25894fe | 2017-05-30 03:40:36 | [diff] [blame] | 8 | #include "base/sequence_checker.h" |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 9 | #include "build/build_config.h" |
| 10 | |
| 11 | #if defined(OS_WIN) |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 12 | #include <windows.h> |
[email protected] | 0ff0ff3 | 2010-12-21 19:34:42 | [diff] [blame] | 13 | #endif // defined(OS_WIN) |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 14 | |
[email protected] | edf04b51 | 2012-02-23 09:47:43 | [diff] [blame] | 15 | #include <set> |
| 16 | #include <vector> |
| 17 | |
[email protected] | 5d36454 | 2012-04-05 07:15:39 | [diff] [blame] | 18 | #include "base/callback.h" |
[email protected] | edf04b51 | 2012-02-23 09:47:43 | [diff] [blame] | 19 | #include "base/command_line.h" |
[email protected] | 5799981 | 2013-02-24 05:40:52 | [diff] [blame] | 20 | #include "base/files/file_path.h" |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 21 | #include "base/logging.h" |
avi | b896c71 | 2015-12-26 02:10:43 | [diff] [blame] | 22 | #include "base/macros.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 23 | #include "base/memory/ref_counted.h" |
[email protected] | d09a4ce1c | 2013-07-24 17:37:02 | [diff] [blame] | 24 | #include "base/process/process.h" |
[email protected] | 08397d5 | 2011-02-05 01:53:38 | [diff] [blame] | 25 | #include "ui/gfx/native_widget_types.h" |
[email protected] | 0ff0ff3 | 2010-12-21 19:34:42 | [diff] [blame] | 26 | |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 27 | #if defined(OS_POSIX) && !defined(OS_ANDROID) |
[email protected] | ea1a3f6 | 2012-11-16 20:34:23 | [diff] [blame] | 28 | #include "base/files/scoped_temp_dir.h" |
[email protected] | a220b593 | 2013-09-21 03:47:44 | [diff] [blame] | 29 | #endif |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 30 | |
[email protected] | 4cf04bb | 2013-07-11 09:22:38 | [diff] [blame] | 31 | #if defined(OS_WIN) |
| 32 | #include "base/win/message_window.h" |
| 33 | #endif // defined(OS_WIN) |
| 34 | |
[email protected] | 2f3b1cc | 2014-03-17 23:07:15 | [diff] [blame] | 35 | namespace base { |
[email protected] | 0189bbd | 2009-10-12 22:50:39 | [diff] [blame] | 36 | class CommandLine; |
[email protected] | 2f3b1cc | 2014-03-17 23:07:15 | [diff] [blame] | 37 | } |
[email protected] | 0189bbd | 2009-10-12 22:50:39 | [diff] [blame] | 38 | |
[email protected] | 7c47ae3e | 2009-02-18 00:34:21 | [diff] [blame] | 39 | // ProcessSingleton ---------------------------------------------------------- |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 40 | // |
[email protected] | 7c47ae3e | 2009-02-18 00:34:21 | [diff] [blame] | 41 | // This class allows different browser processes to communicate with |
| 42 | // each other. It is named according to the user data directory, so |
| 43 | // we can be sure that no more than one copy of the application can be |
| 44 | // running at once with a given data directory. |
| 45 | // |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 46 | // Implementation notes: |
| 47 | // - the Windows implementation uses an invisible global message window; |
[email protected] | e134a72 | 2009-02-23 23:54:02 | [diff] [blame] | 48 | // - the Linux implementation uses a Unix domain socket in the user data dir. |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 49 | |
gab | 25894fe | 2017-05-30 03:40:36 | [diff] [blame] | 50 | class ProcessSingleton { |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 51 | public: |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 52 | // Used to send the reason of remote hang process termination as histogram. |
| 53 | enum RemoteHungProcessTerminateReason { |
| 54 | #if defined(OS_WIN) |
| 55 | USER_ACCEPTED_TERMINATION = 1, |
| 56 | NO_VISIBLE_WINDOW_FOUND = 2, |
| 57 | #elif defined(OS_POSIX) |
| 58 | NOTIFY_ATTEMPTS_EXCEEDED = 3, |
| 59 | SOCKET_WRITE_FAILED = 4, |
| 60 | SOCKET_READ_FAILED = 5, |
| 61 | #endif |
| 62 | REMOTE_HUNG_PROCESS_TERMINATE_REASON_COUNT |
| 63 | }; |
| 64 | |
| 65 | // Used to send the result of interaction with remote process as histograms in |
| 66 | // case when remote process influences on start. |
| 67 | enum RemoteProcessInteractionResult { |
| 68 | TERMINATE_SUCCEEDED = 0, |
| 69 | TERMINATE_FAILED = 1, |
| 70 | REMOTE_PROCESS_NOT_FOUND = 2, |
| 71 | #if defined(OS_WIN) |
| 72 | TERMINATE_WAIT_TIMEOUT = 3, |
| 73 | RUNNING_PROCESS_NOTIFY_ERROR = 4, |
| 74 | #elif defined(OS_POSIX) |
| 75 | TERMINATE_NOT_ENOUGH_PERMISSIONS = 5, |
| 76 | REMOTE_PROCESS_SHUTTING_DOWN = 6, |
| 77 | PROFILE_UNLOCKED = 7, |
| 78 | PROFILE_UNLOCKED_BEFORE_KILL = 8, |
| 79 | SAME_BROWSER_INSTANCE = 9, |
| 80 | SAME_BROWSER_INSTANCE_BEFORE_KILL = 10, |
| 81 | FAILED_TO_EXTRACT_PID = 11, |
| 82 | INVALID_LOCK_FILE = 12, |
| 83 | ORPHANED_LOCK_FILE = 13, |
| 84 | #endif |
Mikhail Atuchin | a6e8bcd3 | 2018-07-06 12:14:03 | [diff] [blame] | 85 | USER_REFUSED_TERMINATION = 14, |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 86 | REMOTE_PROCESS_INTERACTION_RESULT_COUNT |
| 87 | }; |
| 88 | |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 89 | // Logged as histograms, do not modify these values. |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 90 | enum NotifyResult { |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 91 | PROCESS_NONE = 0, |
| 92 | PROCESS_NOTIFIED = 1, |
| 93 | PROFILE_IN_USE = 2, |
| 94 | LOCK_ERROR = 3, |
| 95 | LAST_VALUE = LOCK_ERROR |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 96 | }; |
| 97 | |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 98 | static constexpr int kNumNotifyResults = LAST_VALUE + 1; |
| 99 | |
[email protected] | 5d36454 | 2012-04-05 07:15:39 | [diff] [blame] | 100 | // Implement this callback to handle notifications from other processes. The |
| 101 | // callback will receive the command line and directory with which the other |
| 102 | // Chrome process was launched. Return true if the command line will be |
| 103 | // handled within the current browser instance or false if the remote process |
| 104 | // should handle it (i.e., because the current process is shutting down). |
siggi | 2a0214b | 2015-03-12 14:50:27 | [diff] [blame] | 105 | using NotificationCallback = |
| 106 | base::Callback<bool(const base::CommandLine& command_line, |
| 107 | const base::FilePath& current_directory)>; |
[email protected] | 5d36454 | 2012-04-05 07:15:39 | [diff] [blame] | 108 | |
[email protected] | dd85d45 | 2013-03-28 12:39:59 | [diff] [blame] | 109 | ProcessSingleton(const base::FilePath& user_data_dir, |
| 110 | const NotificationCallback& notification_callback); |
[email protected] | 7c47ae3e | 2009-02-18 00:34:21 | [diff] [blame] | 111 | ~ProcessSingleton(); |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 112 | |
[email protected] | 5d36454 | 2012-04-05 07:15:39 | [diff] [blame] | 113 | // Notify another process, if available. Otherwise sets ourselves as the |
[email protected] | dd85d45 | 2013-03-28 12:39:59 | [diff] [blame] | 114 | // singleton instance. Returns PROCESS_NONE if we became the singleton |
[email protected] | 14649ecd | 2012-12-05 01:00:24 | [diff] [blame] | 115 | // instance. Callers are guaranteed to either have notified an existing |
| 116 | // process or have grabbed the singleton (unless the profile is locked by an |
| 117 | // unreachable process). |
| 118 | // TODO(brettw): Make the implementation of this method non-platform-specific |
| 119 | // by making Linux re-use the Windows implementation. |
[email protected] | dd85d45 | 2013-03-28 12:39:59 | [diff] [blame] | 120 | NotifyResult NotifyOtherProcessOrCreate(); |
[email protected] | 4a44bc3 | 2010-05-28 22:22:44 | [diff] [blame] | 121 | |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 122 | // Sets ourself up as the singleton instance. Returns true on success. If |
| 123 | // false is returned, we are not the singleton instance and the caller must |
[email protected] | dd85d45 | 2013-03-28 12:39:59 | [diff] [blame] | 124 | // exit. |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 125 | // NOTE: Most callers should generally prefer NotifyOtherProcessOrCreate() to |
[email protected] | d4da57be | 2014-07-16 17:53:20 | [diff] [blame] | 126 | // this method, only callers for whom failure is preferred to notifying |
| 127 | // another process should call this directly. |
[email protected] | dd85d45 | 2013-03-28 12:39:59 | [diff] [blame] | 128 | bool Create(); |
[email protected] | c0d29795 | 2009-09-17 21:00:18 | [diff] [blame] | 129 | |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 130 | // Clear any lock state during shutdown. |
| 131 | void Cleanup(); |
| 132 | |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 133 | #if defined(OS_POSIX) && !defined(OS_ANDROID) |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 134 | static void DisablePromptForTesting(); |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 135 | static void SkipIsChromeProcessCheckForTesting(bool skip); |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 136 | static void SetUserOptedUnlockInUseProfileForTesting(bool set_unlock); |
[email protected] | a220b593 | 2013-09-21 03:47:44 | [diff] [blame] | 137 | #endif |
siggi | 2a0214b | 2015-03-12 14:50:27 | [diff] [blame] | 138 | #if defined(OS_WIN) |
| 139 | // Called to query whether to kill a hung browser process that has visible |
| 140 | // windows. Return true to allow killing the hung process. |
| 141 | using ShouldKillRemoteProcessCallback = base::Callback<bool()>; |
| 142 | void OverrideShouldKillRemoteProcessCallbackForTesting( |
| 143 | const ShouldKillRemoteProcessCallback& display_dialog_callback); |
| 144 | #endif |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 145 | |
| 146 | protected: |
| 147 | // Notify another process, if available. |
[email protected] | 14649ecd | 2012-12-05 01:00:24 | [diff] [blame] | 148 | // Returns true if another process was found and notified, false if we should |
| 149 | // continue with the current process. |
| 150 | // On Windows, Create() has to be called before this. |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 151 | NotifyResult NotifyOtherProcess(); |
| 152 | |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 153 | #if defined(OS_POSIX) && !defined(OS_ANDROID) |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 154 | // Exposed for testing. We use a timeout on Linux, and in tests we want |
| 155 | // this timeout to be short. |
[email protected] | 2f3b1cc | 2014-03-17 23:07:15 | [diff] [blame] | 156 | NotifyResult NotifyOtherProcessWithTimeout( |
| 157 | const base::CommandLine& command_line, |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 158 | int retry_attempts, |
| 159 | const base::TimeDelta& timeout, |
[email protected] | 2f3b1cc | 2014-03-17 23:07:15 | [diff] [blame] | 160 | bool kill_unresponsive); |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 161 | NotifyResult NotifyOtherProcessWithTimeoutOrCreate( |
[email protected] | 2f3b1cc | 2014-03-17 23:07:15 | [diff] [blame] | 162 | const base::CommandLine& command_line, |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 163 | int retry_attempts, |
| 164 | const base::TimeDelta& timeout); |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 165 | void OverrideCurrentPidForTesting(base::ProcessId pid); |
| 166 | void OverrideKillCallbackForTesting( |
| 167 | const base::Callback<void(int)>& callback); |
[email protected] | a220b593 | 2013-09-21 03:47:44 | [diff] [blame] | 168 | #endif |
[email protected] | 7707b21c | 2012-12-03 20:42:37 | [diff] [blame] | 169 | |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 170 | private: |
[email protected] | 5d36454 | 2012-04-05 07:15:39 | [diff] [blame] | 171 | NotificationCallback notification_callback_; // Handler for notifications. |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 172 | |
[email protected] | 95259c6 | 2011-10-25 23:23:53 | [diff] [blame] | 173 | #if defined(OS_WIN) |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 174 | bool EscapeVirtualization(const base::FilePath& user_data_dir); |
[email protected] | 0a19455 | 2011-09-14 17:53:35 | [diff] [blame] | 175 | |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 176 | HWND remote_window_; // The HWND_MESSAGE of another browser. |
[email protected] | 4cf04bb | 2013-07-11 09:22:38 | [diff] [blame] | 177 | base::win::MessageWindow window_; // The message-only window. |
[email protected] | 0a19455 | 2011-09-14 17:53:35 | [diff] [blame] | 178 | bool is_virtualized_; // Stuck inside Microsoft Softricity VM environment. |
[email protected] | 2b7ff5b9 | 2012-07-17 22:45:18 | [diff] [blame] | 179 | HANDLE lock_file_; |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 180 | base::FilePath user_data_dir_; |
siggi | 2a0214b | 2015-03-12 14:50:27 | [diff] [blame] | 181 | ShouldKillRemoteProcessCallback should_kill_remote_process_callback_; |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 182 | #elif defined(OS_POSIX) && !defined(OS_ANDROID) |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 183 | // Return true if the given pid is one of our child processes. |
| 184 | // Assumes that the current pid is the root of all pids of the current |
| 185 | // instance. |
| 186 | bool IsSameChromeInstance(pid_t pid); |
| 187 | |
| 188 | // Extract the process's pid from a symbol link path and if it is on |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 189 | // the same host or is_connected_to_socket is true, kill the process, unlink |
| 190 | // the lock file and return true. |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 191 | // If the process is part of the same chrome instance, unlink the lock file |
| 192 | // and return true without killing it. |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 193 | // If the process is on a different host and is_connected_to_socket is false, |
| 194 | // display profile in use error dialog (on Linux). If user opted to unlock |
| 195 | // profile (on Mac OS X by default), unlink the lock file and return true. |
| 196 | // Otherwise return false. |
| 197 | bool KillProcessByLockPath(bool is_connected_to_socket); |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 198 | |
| 199 | // Default function to kill a process, overridable by tests. |
| 200 | void KillProcess(int pid); |
| 201 | |
| 202 | // Allow overriding for tests. |
| 203 | base::ProcessId current_pid_; |
| 204 | |
| 205 | // Function to call when the other process is hung and needs to be killed. |
| 206 | // Allows overriding for tests. |
| 207 | base::Callback<void(int)> kill_callback_; |
| 208 | |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 209 | // Path in file system to the socket. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 210 | base::FilePath socket_path_; |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 211 | |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 212 | // Path in file system to the lock. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 213 | base::FilePath lock_path_; |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 214 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 215 | // Path in file system to the cookie file. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 216 | base::FilePath cookie_path_; |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 217 | |
| 218 | // Temporary directory to hold the socket. |
[email protected] | ea1a3f6 | 2012-11-16 20:34:23 | [diff] [blame] | 219 | base::ScopedTempDir socket_dir_; |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 220 | |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 221 | // Helper class for linux specific messages. LinuxWatcher is ref counted |
| 222 | // because it posts messages between threads. |
| 223 | class LinuxWatcher; |
| 224 | scoped_refptr<LinuxWatcher> watcher_; |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 225 | #endif |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 226 | |
Leonard Grey | fe15df9 | 2017-12-08 17:09:53 | [diff] [blame] | 227 | #if defined(OS_MACOSX) |
| 228 | // macOS 10.13 tries to open a new Chrome instance if a user tries to |
| 229 | // open an external link after Chrome has updated, but not relaunched. |
| 230 | // This method extracts any waiting "open URL" AppleEvent and forwards |
| 231 | // it to the running process. Returns true if an event was found and |
| 232 | // forwarded. |
| 233 | // crbug.com/777863 |
| 234 | bool WaitForAndForwardOpenURLEvent(pid_t event_destination_pid); |
| 235 | #endif |
| 236 | |
gab | 25894fe | 2017-05-30 03:40:36 | [diff] [blame] | 237 | SEQUENCE_CHECKER(sequence_checker_); |
| 238 | |
[email protected] | 7c47ae3e | 2009-02-18 00:34:21 | [diff] [blame] | 239 | DISALLOW_COPY_AND_ASSIGN(ProcessSingleton); |
[email protected] | fc14cef | 2009-01-27 22:17:29 | [diff] [blame] | 240 | }; |
| 241 | |
[email protected] | 175a7a2 | 2009-05-03 15:57:53 | [diff] [blame] | 242 | #endif // CHROME_BROWSER_PROCESS_SINGLETON_H_ |