[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [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] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 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 |
| 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 |
| 9 | // exits. |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 10 | // |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 11 | // Because many networked filesystem implementations do not support unix domain |
| 12 | // sockets, we create the socket in a temporary directory and create a symlink |
| 13 | // in the profile. This temporary directory is no longer bound to the profile, |
| 14 | // and may disappear across a reboot or login to a separate session. To bind |
| 15 | // them, we store a unique cookie in the profile directory, which must also be |
| 16 | // present in the remote directory to connect. The cookie is checked both before |
| 17 | // and after the connection. /tmp is sticky, and different Chrome sessions use |
| 18 | // different cookies. Thus, a matching cookie before and after means the |
| 19 | // connection was to a directory with a valid cookie. |
| 20 | // |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 21 | // We also have a lock file, which is a symlink to a non-existent destination. |
| 22 | // The destination is a string containing the hostname and process id of |
| 23 | // chrome's browser process, eg. "SingletonLock -> example.com-9156". When the |
| 24 | // first copy of chrome exits it will delete the lock file on shutdown, so that |
| 25 | // a different instance on a different host may then use the profile directory. |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 26 | // |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 27 | // If writing to the socket fails, the hostname in the lock is checked to see if |
| 28 | // another instance is running a different host using a shared filesystem (nfs, |
| 29 | // etc.) If the hostname differs an error is displayed and the second process |
| 30 | // exits. Otherwise the first process (if any) is killed and the second process |
| 31 | // starts as normal. |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 32 | // |
| 33 | // When the second process sends the current directory and command line flags to |
| 34 | // the first process, it waits for an ACK message back from the first process |
| 35 | // for a certain time. If there is no ACK message back in time, then the first |
| 36 | // process will be considered as hung for some reason. The second process then |
| 37 | // retrieves the process id from the symbol link and kills it by sending |
| 38 | // SIGKILL. Then the second process starts as normal. |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 39 | |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 40 | #include "chrome/browser/process_singleton.h" |
| 41 | |
| 42 | #include <errno.h> |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 43 | #include <fcntl.h> |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 44 | #include <signal.h> |
[email protected] | 8637807 | 2009-07-31 07:09:34 | [diff] [blame] | 45 | #include <sys/socket.h> |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 46 | #include <sys/stat.h> |
| 47 | #include <sys/types.h> |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 48 | #include <sys/un.h> |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 49 | #include <unistd.h> |
[email protected] | 93270d00 | 2011-01-19 22:32:59 | [diff] [blame] | 50 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 51 | #include <cstring> |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 52 | #include <memory> |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 53 | #include <set> |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 54 | #include <string> |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 55 | |
avi | b896c71 | 2015-12-26 02:10:43 | [diff] [blame] | 56 | #include <stddef.h> |
| 57 | |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 58 | #include "base/base_paths.h" |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 59 | #include "base/bind.h" |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 60 | #include "base/command_line.h" |
David Benjamin | d55612d | 2019-04-02 23:33:05 | [diff] [blame] | 61 | #include "base/containers/unique_ptr_adapters.h" |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 62 | #include "base/files/file_descriptor_watcher_posix.h" |
[email protected] | 5799981 | 2013-02-24 05:40:52 | [diff] [blame] | 63 | #include "base/files/file_path.h" |
thestig | 18dfb7a5 | 2014-08-26 10:44:04 | [diff] [blame] | 64 | #include "base/files/file_util.h" |
skyostil | 0259835 | 2015-06-12 12:37:25 | [diff] [blame] | 65 | #include "base/location.h" |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 66 | #include "base/logging.h" |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 67 | #include "base/memory/ref_counted.h" |
Ilya Sherman | 982457e6 | 2017-12-13 02:19:36 | [diff] [blame] | 68 | #include "base/metrics/histogram_functions.h" |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 69 | #include "base/metrics/histogram_macros.h" |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 70 | #include "base/path_service.h" |
[email protected] | 2025d00 | 2012-11-14 20:54:35 | [diff] [blame] | 71 | #include "base/posix/eintr_wrapper.h" |
brettw | 6ee6fd6 | 2015-06-09 18:05:24 | [diff] [blame] | 72 | #include "base/posix/safe_strerror.h" |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 73 | #include "base/rand_util.h" |
[email protected] | fb44196 | 2013-05-08 05:35:24 | [diff] [blame] | 74 | #include "base/sequenced_task_runner_helpers.h" |
skyostil | 0259835 | 2015-06-12 12:37:25 | [diff] [blame] | 75 | #include "base/single_thread_task_runner.h" |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 76 | #include "base/stl_util.h" |
[email protected] | 3ea1b18 | 2013-02-08 22:38:41 | [diff] [blame] | 77 | #include "base/strings/string_number_conversions.h" |
[email protected] | 1988e1c | 2013-02-28 20:27:42 | [diff] [blame] | 78 | #include "base/strings/string_split.h" |
[email protected] | 539f6b3 | 2014-08-12 02:50:00 | [diff] [blame] | 79 | #include "base/strings/string_util.h" |
[email protected] | 76fb05c | 2013-06-11 04:38:05 | [diff] [blame] | 80 | #include "base/strings/stringprintf.h" |
[email protected] | 3268d7b7 | 2013-03-28 17:41:43 | [diff] [blame] | 81 | #include "base/strings/sys_string_conversions.h" |
[email protected] | e309f31 | 2013-06-07 21:50:08 | [diff] [blame] | 82 | #include "base/strings/utf_string_conversions.h" |
Eric Seckler | 8652dcd5 | 2018-09-20 10:42:28 | [diff] [blame] | 83 | #include "base/task/post_task.h" |
[email protected] | f214f879 | 2011-01-01 02:17:08 | [diff] [blame] | 84 | #include "base/threading/platform_thread.h" |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 85 | #include "base/threading/thread_task_runner_handle.h" |
[email protected] | 8481347 | 2013-06-28 00:25:19 | [diff] [blame] | 86 | #include "base/time/time.h" |
| 87 | #include "base/timer/timer.h" |
avi | b896c71 | 2015-12-26 02:10:43 | [diff] [blame] | 88 | #include "build/build_config.h" |
[email protected] | 1912cfe | 2009-04-21 08:09:30 | [diff] [blame] | 89 | #include "chrome/common/chrome_constants.h" |
Christopher Cameron | 670c4d9 | 2019-07-29 18:39:38 | [diff] [blame] | 90 | #include "chrome/common/process_singleton_lock_posix.h" |
[email protected] | af39f00 | 2014-08-22 10:18:18 | [diff] [blame] | 91 | #include "chrome/grit/chromium_strings.h" |
| 92 | #include "chrome/grit/generated_resources.h" |
Eric Seckler | 8652dcd5 | 2018-09-20 10:42:28 | [diff] [blame] | 93 | #include "content/public/browser/browser_task_traits.h" |
[email protected] | c38831a1 | 2011-10-28 12:44:49 | [diff] [blame] | 94 | #include "content/public/browser/browser_thread.h" |
tfarina | e5f66e6 | 2016-02-12 02:48:40 | [diff] [blame] | 95 | #include "net/base/network_interfaces.h" |
[email protected] | c051a1b | 2011-01-21 23:30:17 | [diff] [blame] | 96 | #include "ui/base/l10n/l10n_util.h" |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 97 | |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 98 | #if defined(OS_LINUX) |
| 99 | #include "chrome/browser/ui/process_singleton_dialog_linux.h" |
| 100 | #endif |
| 101 | |
[email protected] | 1abeec3 | 2014-07-16 05:56:11 | [diff] [blame] | 102 | #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) |
[email protected] | cc04a15 | 2014-02-12 09:17:16 | [diff] [blame] | 103 | #include "ui/views/linux_ui/linux_ui.h" |
| 104 | #endif |
| 105 | |
[email protected] | 631bb74 | 2011-11-02 11:29:39 | [diff] [blame] | 106 | using content::BrowserThread; |
| 107 | |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 108 | namespace { |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 109 | |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 110 | // Timeout for the current browser process to respond. 20 seconds should be |
| 111 | // enough. |
| 112 | const int kTimeoutInSeconds = 20; |
| 113 | // Number of retries to notify the browser. 20 retries over 20 seconds = 1 try |
| 114 | // per second. |
| 115 | const int kRetryAttempts = 20; |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 116 | const char kStartToken[] = "START"; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 117 | const char kACKToken[] = "ACK"; |
| 118 | const char kShutdownToken[] = "SHUTDOWN"; |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 119 | const char kTokenDelimiter = '\0'; |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 120 | const int kMaxMessageLength = 32 * 1024; |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 121 | const int kMaxACKMessageLength = base::size(kShutdownToken) - 1; |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 122 | |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 123 | bool g_disable_prompt = false; |
| 124 | bool g_skip_is_chrome_process_check = false; |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 125 | bool g_user_opted_unlock_in_use_profile = false; |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 126 | |
[email protected] | becc0bb | 2009-08-03 17:29:49 | [diff] [blame] | 127 | // Set the close-on-exec bit on a file descriptor. |
| 128 | // Returns 0 on success, -1 on failure. |
| 129 | int SetCloseOnExec(int fd) { |
| 130 | int flags = fcntl(fd, F_GETFD, 0); |
| 131 | if (-1 == flags) |
| 132 | return flags; |
| 133 | if (flags & FD_CLOEXEC) |
| 134 | return 0; |
| 135 | return fcntl(fd, F_SETFD, flags | FD_CLOEXEC); |
| 136 | } |
| 137 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 138 | // Close a socket and check return value. |
| 139 | void CloseSocket(int fd) { |
[email protected] | d89eec8 | 2013-12-03 14:10:59 | [diff] [blame] | 140 | int rv = IGNORE_EINTR(close(fd)); |
brettw | 6ee6fd6 | 2015-06-09 18:05:24 | [diff] [blame] | 141 | DCHECK_EQ(0, rv) << "Error closing socket: " << base::safe_strerror(errno); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 142 | } |
| 143 | |
| 144 | // Write a message to a socket fd. |
| 145 | bool WriteToSocket(int fd, const char *message, size_t length) { |
| 146 | DCHECK(message); |
| 147 | DCHECK(length); |
| 148 | size_t bytes_written = 0; |
| 149 | do { |
| 150 | ssize_t rv = HANDLE_EINTR( |
| 151 | write(fd, message + bytes_written, length - bytes_written)); |
| 152 | if (rv < 0) { |
| 153 | if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| 154 | // The socket shouldn't block, we're sending so little data. Just give |
| 155 | // up here, since NotifyOtherProcess() doesn't have an asynchronous api. |
| 156 | LOG(ERROR) << "ProcessSingleton would block on write(), so it gave up."; |
| 157 | return false; |
| 158 | } |
[email protected] | 57b76567 | 2009-10-13 18:27:40 | [diff] [blame] | 159 | PLOG(ERROR) << "write() failed"; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 160 | return false; |
| 161 | } |
| 162 | bytes_written += rv; |
| 163 | } while (bytes_written < length); |
| 164 | |
| 165 | return true; |
| 166 | } |
| 167 | |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 168 | struct timeval TimeDeltaToTimeVal(const base::TimeDelta& delta) { |
| 169 | struct timeval result; |
| 170 | result.tv_sec = delta.InSeconds(); |
| 171 | result.tv_usec = delta.InMicroseconds() % base::Time::kMicrosecondsPerSecond; |
| 172 | return result; |
| 173 | } |
| 174 | |
| 175 | // Wait a socket for read for a certain timeout. |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 176 | // Returns -1 if error occurred, 0 if timeout reached, > 0 if the socket is |
| 177 | // ready for read. |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 178 | int WaitSocketForRead(int fd, const base::TimeDelta& timeout) { |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 179 | fd_set read_fds; |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 180 | struct timeval tv = TimeDeltaToTimeVal(timeout); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 181 | |
| 182 | FD_ZERO(&read_fds); |
| 183 | FD_SET(fd, &read_fds); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 184 | |
| 185 | return HANDLE_EINTR(select(fd + 1, &read_fds, NULL, NULL, &tv)); |
| 186 | } |
| 187 | |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 188 | // Read a message from a socket fd, with an optional timeout. |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 189 | // If |timeout| <= 0 then read immediately. |
| 190 | // Return number of bytes actually read, or -1 on error. |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 191 | ssize_t ReadFromSocket(int fd, |
| 192 | char* buf, |
| 193 | size_t bufsize, |
| 194 | const base::TimeDelta& timeout) { |
| 195 | if (timeout > base::TimeDelta()) { |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 196 | int rv = WaitSocketForRead(fd, timeout); |
| 197 | if (rv <= 0) |
| 198 | return rv; |
| 199 | } |
| 200 | |
| 201 | size_t bytes_read = 0; |
| 202 | do { |
| 203 | ssize_t rv = HANDLE_EINTR(read(fd, buf + bytes_read, bufsize - bytes_read)); |
| 204 | if (rv < 0) { |
| 205 | if (errno != EAGAIN && errno != EWOULDBLOCK) { |
[email protected] | 57b76567 | 2009-10-13 18:27:40 | [diff] [blame] | 206 | PLOG(ERROR) << "read() failed"; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 207 | return rv; |
| 208 | } else { |
| 209 | // It would block, so we just return what has been read. |
| 210 | return bytes_read; |
| 211 | } |
| 212 | } else if (!rv) { |
| 213 | // No more data to read. |
| 214 | return bytes_read; |
| 215 | } else { |
| 216 | bytes_read += rv; |
| 217 | } |
| 218 | } while (bytes_read < bufsize); |
| 219 | |
| 220 | return bytes_read; |
| 221 | } |
| 222 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 223 | // Set up a sockaddr appropriate for messaging. |
mattm | 916edf16 | 2017-04-13 21:26:51 | [diff] [blame] | 224 | bool SetupSockAddr(const std::string& path, struct sockaddr_un* addr) { |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 225 | addr->sun_family = AF_UNIX; |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 226 | if (path.length() >= base::size(addr->sun_path)) |
mattm | 916edf16 | 2017-04-13 21:26:51 | [diff] [blame] | 227 | return false; |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 228 | base::strlcpy(addr->sun_path, path.c_str(), base::size(addr->sun_path)); |
mattm | 916edf16 | 2017-04-13 21:26:51 | [diff] [blame] | 229 | return true; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 230 | } |
| 231 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 232 | // Set up a socket appropriate for messaging. |
| 233 | int SetupSocketOnly() { |
| 234 | int sock = socket(PF_UNIX, SOCK_STREAM, 0); |
| 235 | PCHECK(sock >= 0) << "socket() failed"; |
| 236 | |
tfarina | 060df7e | 2015-12-16 05:15:32 | [diff] [blame] | 237 | DCHECK(base::SetNonBlocking(sock)) << "Failed to make non-blocking socket."; |
| 238 | int rv = SetCloseOnExec(sock); |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 239 | DCHECK_EQ(0, rv) << "Failed to set CLOEXEC on socket."; |
| 240 | |
| 241 | return sock; |
| 242 | } |
| 243 | |
| 244 | // Set up a socket and sockaddr appropriate for messaging. |
| 245 | void SetupSocket(const std::string& path, int* sock, struct sockaddr_un* addr) { |
| 246 | *sock = SetupSocketOnly(); |
mattm | 916edf16 | 2017-04-13 21:26:51 | [diff] [blame] | 247 | CHECK(SetupSockAddr(path, addr)) << "Socket path too long: " << path; |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 248 | } |
| 249 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 250 | // Read a symbolic link, return empty string if given path is not a symbol link. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 251 | base::FilePath ReadLink(const base::FilePath& path) { |
| 252 | base::FilePath target; |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 253 | if (!base::ReadSymbolicLink(path, &target)) { |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 254 | // The only errno that should occur is ENOENT. |
| 255 | if (errno != 0 && errno != ENOENT) |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 256 | PLOG(ERROR) << "readlink(" << path.value() << ") failed"; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 257 | } |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 258 | return target; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 259 | } |
| 260 | |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 261 | // Unlink a path. Return true on success. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 262 | bool UnlinkPath(const base::FilePath& path) { |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 263 | int rv = unlink(path.value().c_str()); |
[email protected] | 31466778 | 2009-09-14 22:34:42 | [diff] [blame] | 264 | if (rv < 0 && errno != ENOENT) |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 265 | PLOG(ERROR) << "Failed to unlink " << path.value(); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 266 | |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 267 | return rv == 0; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 268 | } |
| 269 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 270 | // Create a symlink. Returns true on success. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 271 | bool SymlinkPath(const base::FilePath& target, const base::FilePath& path) { |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 272 | if (!base::CreateSymbolicLink(target, path)) { |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 273 | // Double check the value in case symlink suceeded but we got an incorrect |
| 274 | // failure due to NFS packet loss & retry. |
| 275 | int saved_errno = errno; |
| 276 | if (ReadLink(path) != target) { |
| 277 | // If we failed to create the lock, most likely another instance won the |
| 278 | // startup race. |
| 279 | errno = saved_errno; |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 280 | PLOG(ERROR) << "Failed to create " << path.value(); |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 281 | return false; |
| 282 | } |
| 283 | } |
| 284 | return true; |
| 285 | } |
| 286 | |
[email protected] | 37248d7 | 2013-09-11 04:31:27 | [diff] [blame] | 287 | // Returns true if the user opted to unlock the profile. |
| 288 | bool DisplayProfileInUseError(const base::FilePath& lock_path, |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 289 | const std::string& hostname, |
| 290 | int pid) { |
[email protected] | 0085863a | 2013-12-06 21:19:03 | [diff] [blame] | 291 | base::string16 error = l10n_util::GetStringFUTF16( |
Raul Tambre | fff51b75 | 2019-02-04 13:09:47 | [diff] [blame] | 292 | IDS_PROFILE_IN_USE_POSIX, base::NumberToString16(pid), |
[email protected] | 6778fed | 2013-12-24 20:09:37 | [diff] [blame] | 293 | base::ASCIIToUTF16(hostname)); |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 294 | LOG(ERROR) << error; |
| 295 | |
| 296 | if (g_disable_prompt) |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 297 | return g_user_opted_unlock_in_use_profile; |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 298 | |
| 299 | #if defined(OS_LINUX) |
[email protected] | 0085863a | 2013-12-06 21:19:03 | [diff] [blame] | 300 | base::string16 relaunch_button_text = l10n_util::GetStringUTF16( |
[email protected] | 37248d7 | 2013-09-11 04:31:27 | [diff] [blame] | 301 | IDS_PROFILE_IN_USE_LINUX_RELAUNCH); |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 302 | return ShowProcessSingletonDialog(error, relaunch_button_text); |
| 303 | #elif defined(OS_MACOSX) |
| 304 | // On Mac, always usurp the lock. |
| 305 | return true; |
| 306 | #endif |
| 307 | |
| 308 | NOTREACHED(); |
[email protected] | 37248d7 | 2013-09-11 04:31:27 | [diff] [blame] | 309 | return false; |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 310 | } |
| 311 | |
[email protected] | a70d9cf | 2010-05-11 23:05:19 | [diff] [blame] | 312 | bool IsChromeProcess(pid_t pid) { |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 313 | if (g_skip_is_chrome_process_check) |
| 314 | return true; |
| 315 | |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 316 | base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid)); |
[email protected] | a70d9cf | 2010-05-11 23:05:19 | [diff] [blame] | 317 | return (!other_chrome_path.empty() && |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 318 | other_chrome_path.BaseName() == |
| 319 | base::FilePath(chrome::kBrowserProcessExecutableName)); |
[email protected] | a70d9cf | 2010-05-11 23:05:19 | [diff] [blame] | 320 | } |
| 321 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 322 | // A helper class to hold onto a socket. |
| 323 | class ScopedSocket { |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 324 | public: |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 325 | ScopedSocket() : fd_(-1) { Reset(); } |
| 326 | ~ScopedSocket() { Close(); } |
| 327 | int fd() { return fd_; } |
| 328 | void Reset() { |
| 329 | Close(); |
| 330 | fd_ = SetupSocketOnly(); |
| 331 | } |
| 332 | void Close() { |
| 333 | if (fd_ >= 0) |
| 334 | CloseSocket(fd_); |
| 335 | fd_ = -1; |
| 336 | } |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 337 | private: |
| 338 | int fd_; |
| 339 | }; |
| 340 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 341 | // Returns a random string for uniquifying profile connections. |
| 342 | std::string GenerateCookie() { |
Daniel Cheng | 3d199b1 | 2017-12-12 03:51:09 | [diff] [blame] | 343 | return base::NumberToString(base::RandUint64()); |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 344 | } |
| 345 | |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 346 | bool CheckCookie(const base::FilePath& path, const base::FilePath& cookie) { |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 347 | return (cookie == ReadLink(path)); |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | bool ConnectSocket(ScopedSocket* socket, |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 351 | const base::FilePath& socket_path, |
| 352 | const base::FilePath& cookie_path) { |
| 353 | base::FilePath socket_target; |
[email protected] | b264eab | 2013-11-27 23:22:08 | [diff] [blame] | 354 | if (base::ReadSymbolicLink(socket_path, &socket_target)) { |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 355 | // It's a symlink. Read the cookie. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 356 | base::FilePath cookie = ReadLink(cookie_path); |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 357 | if (cookie.empty()) |
| 358 | return false; |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 359 | base::FilePath remote_cookie = socket_target.DirName(). |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 360 | Append(chrome::kSingletonCookieFilename); |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 361 | // Verify the cookie before connecting. |
| 362 | if (!CheckCookie(remote_cookie, cookie)) |
| 363 | return false; |
| 364 | // Now we know the directory was (at that point) created by the profile |
| 365 | // owner. Try to connect. |
| 366 | sockaddr_un addr; |
mattm | 916edf16 | 2017-04-13 21:26:51 | [diff] [blame] | 367 | if (!SetupSockAddr(socket_target.value(), &addr)) { |
| 368 | // If a sockaddr couldn't be initialized due to too long of a socket |
| 369 | // path, we can be sure there isn't already a Chrome running with this |
| 370 | // socket path, since it would have hit the CHECK() on the path length. |
| 371 | return false; |
| 372 | } |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 373 | int ret = HANDLE_EINTR(connect(socket->fd(), |
| 374 | reinterpret_cast<sockaddr*>(&addr), |
| 375 | sizeof(addr))); |
| 376 | if (ret != 0) |
| 377 | return false; |
| 378 | // Check the cookie again. We only link in /tmp, which is sticky, so, if the |
| 379 | // directory is still correct, it must have been correct in-between when we |
| 380 | // connected. POSIX, sadly, lacks a connectat(). |
| 381 | if (!CheckCookie(remote_cookie, cookie)) { |
| 382 | socket->Reset(); |
| 383 | return false; |
| 384 | } |
| 385 | // Success! |
| 386 | return true; |
| 387 | } else if (errno == EINVAL) { |
| 388 | // It exists, but is not a symlink (or some other error we detect |
| 389 | // later). Just connect to it directly; this is an older version of Chrome. |
| 390 | sockaddr_un addr; |
mattm | 916edf16 | 2017-04-13 21:26:51 | [diff] [blame] | 391 | if (!SetupSockAddr(socket_path.value(), &addr)) { |
| 392 | // If a sockaddr couldn't be initialized due to too long of a socket |
| 393 | // path, we can be sure there isn't already a Chrome running with this |
| 394 | // socket path, since it would have hit the CHECK() on the path length. |
| 395 | return false; |
| 396 | } |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 397 | int ret = HANDLE_EINTR(connect(socket->fd(), |
| 398 | reinterpret_cast<sockaddr*>(&addr), |
| 399 | sizeof(addr))); |
| 400 | return (ret == 0); |
| 401 | } else { |
| 402 | // File is missing, or other error. |
| 403 | if (errno != ENOENT) |
| 404 | PLOG(ERROR) << "readlink failed"; |
| 405 | return false; |
| 406 | } |
| 407 | } |
| 408 | |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 409 | #if defined(OS_MACOSX) |
| 410 | bool ReplaceOldSingletonLock(const base::FilePath& symlink_content, |
| 411 | const base::FilePath& lock_path) { |
| 412 | // Try taking an flock(2) on the file. Failure means the lock is taken so we |
| 413 | // should quit. |
| 414 | base::ScopedFD lock_fd(HANDLE_EINTR( |
| 415 | open(lock_path.value().c_str(), O_RDWR | O_CREAT | O_SYMLINK, 0644))); |
| 416 | if (!lock_fd.is_valid()) { |
| 417 | PLOG(ERROR) << "Could not open singleton lock"; |
| 418 | return false; |
| 419 | } |
| 420 | |
| 421 | int rc = HANDLE_EINTR(flock(lock_fd.get(), LOCK_EX | LOCK_NB)); |
| 422 | if (rc == -1) { |
| 423 | if (errno == EWOULDBLOCK) { |
| 424 | LOG(ERROR) << "Singleton lock held by old process."; |
| 425 | } else { |
| 426 | PLOG(ERROR) << "Error locking singleton lock"; |
| 427 | } |
| 428 | return false; |
| 429 | } |
| 430 | |
| 431 | // Successfully taking the lock means we can replace it with the a new symlink |
| 432 | // lock. We never flock() the lock file from now on. I.e. we assume that an |
| 433 | // old version of Chrome will not run with the same user data dir after this |
| 434 | // version has run. |
| 435 | if (!base::DeleteFile(lock_path, false)) { |
| 436 | PLOG(ERROR) << "Could not delete old singleton lock."; |
| 437 | return false; |
| 438 | } |
| 439 | |
| 440 | return SymlinkPath(symlink_content, lock_path); |
| 441 | } |
| 442 | #endif // defined(OS_MACOSX) |
| 443 | |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 444 | void SendRemoteProcessInteractionResultHistogram( |
| 445 | ProcessSingleton::RemoteProcessInteractionResult result) { |
| 446 | UMA_HISTOGRAM_ENUMERATION( |
| 447 | "Chrome.ProcessSingleton.RemoteProcessInteractionResult", result, |
| 448 | ProcessSingleton::REMOTE_PROCESS_INTERACTION_RESULT_COUNT); |
| 449 | } |
| 450 | |
| 451 | void SendRemoteHungProcessTerminateReasonHistogram( |
| 452 | ProcessSingleton::RemoteHungProcessTerminateReason reason) { |
| 453 | UMA_HISTOGRAM_ENUMERATION( |
| 454 | "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason", reason, |
| 455 | ProcessSingleton::REMOTE_HUNG_PROCESS_TERMINATE_REASON_COUNT); |
| 456 | } |
| 457 | |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 458 | } // namespace |
| 459 | |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 460 | /////////////////////////////////////////////////////////////////////////////// |
| 461 | // ProcessSingleton::LinuxWatcher |
| 462 | // A helper class for a Linux specific implementation of the process singleton. |
| 463 | // This class sets up a listener on the singleton socket and handles parsing |
| 464 | // messages that come in on the singleton socket. |
| 465 | class ProcessSingleton::LinuxWatcher |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 466 | : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher, |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 467 | BrowserThread::DeleteOnIOThread> { |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 468 | public: |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 469 | // A helper class to read message from an established socket. |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 470 | class SocketReader { |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 471 | public: |
| 472 | SocketReader(ProcessSingleton::LinuxWatcher* parent, |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 473 | scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 474 | int fd) |
| 475 | : parent_(parent), |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 476 | ui_task_runner_(ui_task_runner), |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 477 | fd_(fd), |
| 478 | bytes_read_(0) { |
thestig | 00844cea | 2015-09-08 21:44:52 | [diff] [blame] | 479 | DCHECK_CURRENTLY_ON(BrowserThread::IO); |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 480 | // Wait for reads. |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 481 | fd_watch_controller_ = base::FileDescriptorWatcher::WatchReadable( |
| 482 | fd, base::Bind(&SocketReader::OnSocketCanReadWithoutBlocking, |
| 483 | base::Unretained(this))); |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 484 | // If we haven't completed in a reasonable amount of time, give up. |
[email protected] | d323a17 | 2011-09-02 18:23:02 | [diff] [blame] | 485 | timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeoutInSeconds), |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 486 | this, &SocketReader::CleanupAndDeleteSelf); |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 487 | } |
| 488 | |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 489 | ~SocketReader() { CloseSocket(fd_); } |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 490 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 491 | // Finish handling the incoming message by optionally sending back an ACK |
| 492 | // message and removing this SocketReader. |
| 493 | void FinishWithACK(const char *message, size_t length); |
| 494 | |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 495 | private: |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 496 | void OnSocketCanReadWithoutBlocking(); |
| 497 | |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 498 | void CleanupAndDeleteSelf() { |
thestig | 00844cea | 2015-09-08 21:44:52 | [diff] [blame] | 499 | DCHECK_CURRENTLY_ON(BrowserThread::IO); |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 500 | |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 501 | parent_->RemoveSocketReader(this); |
| 502 | // We're deleted beyond this point. |
| 503 | } |
| 504 | |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 505 | // Controls watching |fd_|. |
| 506 | std::unique_ptr<base::FileDescriptorWatcher::Controller> |
| 507 | fd_watch_controller_; |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 508 | |
| 509 | // The ProcessSingleton::LinuxWatcher that owns us. |
| 510 | ProcessSingleton::LinuxWatcher* const parent_; |
| 511 | |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 512 | // A reference to the UI task runner. |
| 513 | scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 514 | |
| 515 | // The file descriptor we're reading. |
| 516 | const int fd_; |
| 517 | |
| 518 | // Store the message in this buffer. |
| 519 | char buf_[kMaxMessageLength]; |
| 520 | |
| 521 | // Tracks the number of bytes we've read in case we're getting partial |
| 522 | // reads. |
| 523 | size_t bytes_read_; |
| 524 | |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 525 | base::OneShotTimer timer_; |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 526 | |
| 527 | DISALLOW_COPY_AND_ASSIGN(SocketReader); |
| 528 | }; |
| 529 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 530 | // We expect to only be constructed on the UI thread. |
| 531 | explicit LinuxWatcher(ProcessSingleton* parent) |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 532 | : ui_task_runner_(base::ThreadTaskRunnerHandle::Get()), parent_(parent) {} |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 533 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 534 | // Start listening for connections on the socket. This method should be |
| 535 | // called from the IO thread. |
| 536 | void StartListening(int socket); |
| 537 | |
| 538 | // This method determines if we should use the same process and if we should, |
| 539 | // opens a new browser tab. This runs on the UI thread. |
| 540 | // |reader| is for sending back ACK message. |
| 541 | void HandleMessage(const std::string& current_dir, |
| 542 | const std::vector<std::string>& argv, |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 543 | SocketReader* reader); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 544 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 545 | private: |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 546 | friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>; |
[email protected] | cff06c57 | 2012-01-02 20:03:05 | [diff] [blame] | 547 | friend class base::DeleteHelper<ProcessSingleton::LinuxWatcher>; |
[email protected] | 8de85a6 | 2009-11-06 08:32:17 | [diff] [blame] | 548 | |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 549 | ~LinuxWatcher() { |
thestig | 00844cea | 2015-09-08 21:44:52 | [diff] [blame] | 550 | DCHECK_CURRENTLY_ON(BrowserThread::IO); |
[email protected] | 8de85a6 | 2009-11-06 08:32:17 | [diff] [blame] | 551 | } |
| 552 | |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 553 | void OnSocketCanReadWithoutBlocking(int socket); |
| 554 | |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 555 | // Removes and deletes the SocketReader. |
| 556 | void RemoveSocketReader(SocketReader* reader); |
| 557 | |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 558 | std::unique_ptr<base::FileDescriptorWatcher::Controller> socket_watcher_; |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 559 | |
| 560 | // A reference to the UI message loop (i.e., the message loop we were |
| 561 | // constructed on). |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 562 | scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 563 | |
| 564 | // The ProcessSingleton that owns us. |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 565 | ProcessSingleton* const parent_; |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 566 | |
David Benjamin | d55612d | 2019-04-02 23:33:05 | [diff] [blame] | 567 | std::set<std::unique_ptr<SocketReader>, base::UniquePtrComparator> readers_; |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 568 | |
| 569 | DISALLOW_COPY_AND_ASSIGN(LinuxWatcher); |
| 570 | }; |
| 571 | |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 572 | void ProcessSingleton::LinuxWatcher::OnSocketCanReadWithoutBlocking( |
| 573 | int socket) { |
thestig | 00844cea | 2015-09-08 21:44:52 | [diff] [blame] | 574 | DCHECK_CURRENTLY_ON(BrowserThread::IO); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 575 | // Accepting incoming client. |
| 576 | sockaddr_un from; |
| 577 | socklen_t from_len = sizeof(from); |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 578 | int connection_socket = HANDLE_EINTR( |
| 579 | accept(socket, reinterpret_cast<sockaddr*>(&from), &from_len)); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 580 | if (-1 == connection_socket) { |
[email protected] | 57b76567 | 2009-10-13 18:27:40 | [diff] [blame] | 581 | PLOG(ERROR) << "accept() failed"; |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 582 | return; |
| 583 | } |
tfarina | 060df7e | 2015-12-16 05:15:32 | [diff] [blame] | 584 | DCHECK(base::SetNonBlocking(connection_socket)) |
| 585 | << "Failed to make non-blocking socket."; |
avi | d2c61e4 | 2016-10-25 00:46:57 | [diff] [blame] | 586 | readers_.insert( |
Jeremy Roman | ec48d7a | 2018-03-01 17:35:09 | [diff] [blame] | 587 | std::make_unique<SocketReader>(this, ui_task_runner_, connection_socket)); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 588 | } |
| 589 | |
[email protected] | b7c5c4c | 2009-05-21 15:55:16 | [diff] [blame] | 590 | void ProcessSingleton::LinuxWatcher::StartListening(int socket) { |
thestig | 00844cea | 2015-09-08 21:44:52 | [diff] [blame] | 591 | DCHECK_CURRENTLY_ON(BrowserThread::IO); |
[email protected] | b7c5c4c | 2009-05-21 15:55:16 | [diff] [blame] | 592 | // Watch for client connections on this socket. |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 593 | socket_watcher_ = base::FileDescriptorWatcher::WatchReadable( |
| 594 | socket, base::Bind(&LinuxWatcher::OnSocketCanReadWithoutBlocking, |
| 595 | base::Unretained(this), socket)); |
[email protected] | b7c5c4c | 2009-05-21 15:55:16 | [diff] [blame] | 596 | } |
| 597 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 598 | void ProcessSingleton::LinuxWatcher::HandleMessage( |
| 599 | const std::string& current_dir, const std::vector<std::string>& argv, |
| 600 | SocketReader* reader) { |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 601 | DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 602 | DCHECK(reader); |
[email protected] | cebf3196 | 2009-10-14 20:16:23 | [diff] [blame] | 603 | |
avi | 556c0502 | 2014-12-22 23:31:43 | [diff] [blame] | 604 | if (parent_->notification_callback_.Run(base::CommandLine(argv), |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 605 | base::FilePath(current_dir))) { |
[email protected] | 5d36454 | 2012-04-05 07:15:39 | [diff] [blame] | 606 | // Send back "ACK" message to prevent the client process from starting up. |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 607 | reader->FinishWithACK(kACKToken, base::size(kACKToken) - 1); |
[email protected] | 5d36454 | 2012-04-05 07:15:39 | [diff] [blame] | 608 | } else { |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 609 | LOG(WARNING) << "Not handling interprocess notification as browser" |
| 610 | " is shutting down"; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 611 | // Send back "SHUTDOWN" message, so that the client process can start up |
| 612 | // without killing this process. |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 613 | reader->FinishWithACK(kShutdownToken, base::size(kShutdownToken) - 1); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 614 | return; |
| 615 | } |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 616 | } |
| 617 | |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 618 | void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) { |
thestig | 00844cea | 2015-09-08 21:44:52 | [diff] [blame] | 619 | DCHECK_CURRENTLY_ON(BrowserThread::IO); |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 620 | DCHECK(reader); |
David Benjamin | d55612d | 2019-04-02 23:33:05 | [diff] [blame] | 621 | auto it = readers_.find(reader); |
avi | d2c61e4 | 2016-10-25 00:46:57 | [diff] [blame] | 622 | readers_.erase(it); |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 623 | } |
| 624 | |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 625 | /////////////////////////////////////////////////////////////////////////////// |
| 626 | // ProcessSingleton::LinuxWatcher::SocketReader |
| 627 | // |
| 628 | |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 629 | void ProcessSingleton::LinuxWatcher::SocketReader:: |
| 630 | OnSocketCanReadWithoutBlocking() { |
thestig | 00844cea | 2015-09-08 21:44:52 | [diff] [blame] | 631 | DCHECK_CURRENTLY_ON(BrowserThread::IO); |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 632 | while (bytes_read_ < sizeof(buf_)) { |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 633 | ssize_t rv = |
| 634 | HANDLE_EINTR(read(fd_, buf_ + bytes_read_, sizeof(buf_) - bytes_read_)); |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 635 | if (rv < 0) { |
| 636 | if (errno != EAGAIN && errno != EWOULDBLOCK) { |
[email protected] | 57b76567 | 2009-10-13 18:27:40 | [diff] [blame] | 637 | PLOG(ERROR) << "read() failed"; |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 638 | CloseSocket(fd_); |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 639 | return; |
| 640 | } else { |
| 641 | // It would block, so we just return and continue to watch for the next |
| 642 | // opportunity to read. |
| 643 | return; |
| 644 | } |
| 645 | } else if (!rv) { |
| 646 | // No more data to read. It's time to process the message. |
| 647 | break; |
| 648 | } else { |
| 649 | bytes_read_ += rv; |
| 650 | } |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 651 | } |
| 652 | |
[email protected] | 52db4aa | 2009-05-21 18:41:02 | [diff] [blame] | 653 | // Validate the message. The shortest message is kStartToken\0x\0x |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 654 | const size_t kMinMessageLength = base::size(kStartToken) + 4; |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 655 | if (bytes_read_ < kMinMessageLength) { |
| 656 | buf_[bytes_read_] = 0; |
| 657 | LOG(ERROR) << "Invalid socket message (wrong length):" << buf_; |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 658 | CleanupAndDeleteSelf(); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 659 | return; |
| 660 | } |
| 661 | |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 662 | std::string str(buf_, bytes_read_); |
brettw | c6f82b1 | 2015-07-21 21:37:38 | [diff] [blame] | 663 | std::vector<std::string> tokens = base::SplitString( |
| 664 | str, std::string(1, kTokenDelimiter), |
| 665 | base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 666 | |
| 667 | if (tokens.size() < 3 || tokens[0] != kStartToken) { |
| 668 | LOG(ERROR) << "Wrong message format: " << str; |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 669 | CleanupAndDeleteSelf(); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 670 | return; |
| 671 | } |
| 672 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 673 | // Stop the expiration timer to prevent this SocketReader object from being |
| 674 | // terminated unexpectly. |
| 675 | timer_.Stop(); |
| 676 | |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 677 | std::string current_dir = tokens[1]; |
| 678 | // Remove the first two tokens. The remaining tokens should be the command |
| 679 | // line argv array. |
| 680 | tokens.erase(tokens.begin()); |
| 681 | tokens.erase(tokens.begin()); |
| 682 | |
| 683 | // Return to the UI thread to handle opening a new browser tab. |
fdoray | c16c6f8 | 2016-06-29 15:27:32 | [diff] [blame] | 684 | ui_task_runner_->PostTask( |
tzik | 3f7781d | 2017-04-20 17:09:33 | [diff] [blame] | 685 | FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage, |
| 686 | parent_, current_dir, tokens, this)); |
fdoray | 00285d7 | 2016-10-14 15:14:20 | [diff] [blame] | 687 | fd_watch_controller_.reset(); |
[email protected] | 8e97eb2d | 2009-05-22 23:01:02 | [diff] [blame] | 688 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 689 | // LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader |
| 690 | // object by invoking SocketReader::FinishWithACK(). |
| 691 | } |
| 692 | |
| 693 | void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK( |
| 694 | const char *message, size_t length) { |
| 695 | if (message && length) { |
| 696 | // Not necessary to care about the return value. |
| 697 | WriteToSocket(fd_, message, length); |
| 698 | } |
| 699 | |
| 700 | if (shutdown(fd_, SHUT_WR) < 0) |
[email protected] | 57b76567 | 2009-10-13 18:27:40 | [diff] [blame] | 701 | PLOG(ERROR) << "shutdown() failed"; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 702 | |
Sami Kyostila | 7d640eb | 2019-07-31 18:50:26 | [diff] [blame] | 703 | base::PostTask( |
Eric Seckler | 8652dcd5 | 2018-09-20 10:42:28 | [diff] [blame] | 704 | FROM_HERE, {BrowserThread::IO}, |
tzik | 3f7781d | 2017-04-20 17:09:33 | [diff] [blame] | 705 | base::BindOnce(&ProcessSingleton::LinuxWatcher::RemoveSocketReader, |
| 706 | parent_, this)); |
[email protected] | 4b3fe63c | 2011-11-17 00:53:03 | [diff] [blame] | 707 | // We will be deleted once the posted RemoveSocketReader task runs. |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 708 | } |
| 709 | |
| 710 | /////////////////////////////////////////////////////////////////////////////// |
| 711 | // ProcessSingleton |
| 712 | // |
[email protected] | dd85d45 | 2013-03-28 12:39:59 | [diff] [blame] | 713 | ProcessSingleton::ProcessSingleton( |
| 714 | const base::FilePath& user_data_dir, |
| 715 | const NotificationCallback& notification_callback) |
[email protected] | 9a47c43 | 2013-04-19 20:33:55 | [diff] [blame] | 716 | : notification_callback_(notification_callback), |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 717 | current_pid_(base::GetCurrentProcId()), |
[email protected] | 9c00909 | 2013-05-01 03:14:09 | [diff] [blame] | 718 | watcher_(new LinuxWatcher(this)) { |
[email protected] | 1912cfe | 2009-04-21 08:09:30 | [diff] [blame] | 719 | socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename); |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 720 | lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename); |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 721 | cookie_path_ = user_data_dir.Append(chrome::kSingletonCookieFilename); |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 722 | |
| 723 | kill_callback_ = base::Bind(&ProcessSingleton::KillProcess, |
| 724 | base::Unretained(this)); |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 725 | } |
| 726 | |
| 727 | ProcessSingleton::~ProcessSingleton() { |
gab | 25894fe | 2017-05-30 03:40:36 | [diff] [blame] | 728 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 729 | } |
| 730 | |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 731 | ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 732 | return NotifyOtherProcessWithTimeout( |
avi | 556c0502 | 2014-12-22 23:31:43 | [diff] [blame] | 733 | *base::CommandLine::ForCurrentProcess(), kRetryAttempts, |
| 734 | base::TimeDelta::FromSeconds(kTimeoutInSeconds), true); |
[email protected] | c0d29795 | 2009-09-17 21:00:18 | [diff] [blame] | 735 | } |
| 736 | |
| 737 | ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout( |
avi | 556c0502 | 2014-12-22 23:31:43 | [diff] [blame] | 738 | const base::CommandLine& cmd_line, |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 739 | int retry_attempts, |
| 740 | const base::TimeDelta& timeout, |
[email protected] | 4a44bc3 | 2010-05-28 22:22:44 | [diff] [blame] | 741 | bool kill_unresponsive) { |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 742 | DCHECK_GE(retry_attempts, 0); |
| 743 | DCHECK_GE(timeout.InMicroseconds(), 0); |
| 744 | |
| 745 | base::TimeDelta sleep_interval = timeout / retry_attempts; |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 746 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 747 | ScopedSocket socket; |
Leonard Grey | fe15df9 | 2017-12-08 17:09:53 | [diff] [blame] | 748 | int pid = 0; |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 749 | for (int retries = 0; retries <= retry_attempts; ++retries) { |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 750 | // Try to connect to the socket. |
Leonard Grey | fe15df9 | 2017-12-08 17:09:53 | [diff] [blame] | 751 | if (ConnectSocket(&socket, socket_path_, cookie_path_)) { |
| 752 | #if defined(OS_MACOSX) |
| 753 | // On Mac, we want the open process' pid in case there are |
| 754 | // Apple Events to forward. See crbug.com/777863. |
| 755 | std::string hostname; |
Christopher Cameron | 670c4d9 | 2019-07-29 18:39:38 | [diff] [blame] | 756 | ParseProcessSingletonLock(lock_path_, &hostname, &pid); |
Leonard Grey | fe15df9 | 2017-12-08 17:09:53 | [diff] [blame] | 757 | #endif |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 758 | break; |
Leonard Grey | fe15df9 | 2017-12-08 17:09:53 | [diff] [blame] | 759 | } |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 760 | |
| 761 | // If we're in a race with another process, they may be in Create() and have |
| 762 | // created the lock but not attached to the socket. So we check if the |
| 763 | // process with the pid from the lockfile is currently running and is a |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 764 | // chrome browser. If so, we loop and try again for |timeout|. |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 765 | |
| 766 | std::string hostname; |
Christopher Cameron | 670c4d9 | 2019-07-29 18:39:38 | [diff] [blame] | 767 | if (!ParseProcessSingletonLock(lock_path_, &hostname, &pid)) { |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 768 | // No lockfile exists. |
| 769 | return PROCESS_NONE; |
| 770 | } |
| 771 | |
| 772 | if (hostname.empty()) { |
| 773 | // Invalid lockfile. |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 774 | UnlinkPath(lock_path_); |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 775 | SendRemoteProcessInteractionResultHistogram(INVALID_LOCK_FILE); |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 776 | return PROCESS_NONE; |
| 777 | } |
| 778 | |
[email protected] | 37248d7 | 2013-09-11 04:31:27 | [diff] [blame] | 779 | if (hostname != net::GetHostName() && !IsChromeProcess(pid)) { |
| 780 | // Locked by process on another host. If the user selected to unlock |
| 781 | // the profile, try to continue; otherwise quit. |
| 782 | if (DisplayProfileInUseError(lock_path_, hostname, pid)) { |
| 783 | UnlinkPath(lock_path_); |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 784 | SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED); |
[email protected] | 37248d7 | 2013-09-11 04:31:27 | [diff] [blame] | 785 | return PROCESS_NONE; |
| 786 | } |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 787 | return PROFILE_IN_USE; |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 788 | } |
| 789 | |
[email protected] | a70d9cf | 2010-05-11 23:05:19 | [diff] [blame] | 790 | if (!IsChromeProcess(pid)) { |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 791 | // Orphaned lockfile (no process with pid, or non-chrome process.) |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 792 | UnlinkPath(lock_path_); |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 793 | SendRemoteProcessInteractionResultHistogram(ORPHANED_LOCK_FILE); |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 794 | return PROCESS_NONE; |
| 795 | } |
| 796 | |
[email protected] | a70d9cf | 2010-05-11 23:05:19 | [diff] [blame] | 797 | if (IsSameChromeInstance(pid)) { |
| 798 | // Orphaned lockfile (pid is part of same chrome instance we are, even |
| 799 | // though we haven't tried to create a lockfile yet). |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 800 | UnlinkPath(lock_path_); |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 801 | SendRemoteProcessInteractionResultHistogram(SAME_BROWSER_INSTANCE); |
[email protected] | a70d9cf | 2010-05-11 23:05:19 | [diff] [blame] | 802 | return PROCESS_NONE; |
| 803 | } |
| 804 | |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 805 | if (retries == retry_attempts) { |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 806 | // Retries failed. Kill the unresponsive chrome process and continue. |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 807 | if (!kill_unresponsive || !KillProcessByLockPath(false)) |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 808 | return PROFILE_IN_USE; |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 809 | SendRemoteHungProcessTerminateReasonHistogram(NOTIFY_ATTEMPTS_EXCEEDED); |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 810 | return PROCESS_NONE; |
| 811 | } |
| 812 | |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 813 | base::PlatformThread::Sleep(sleep_interval); |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 814 | } |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 815 | |
Leonard Grey | fe15df9 | 2017-12-08 17:09:53 | [diff] [blame] | 816 | #if defined(OS_MACOSX) |
| 817 | if (pid > 0 && WaitForAndForwardOpenURLEvent(pid)) { |
| 818 | return PROCESS_NOTIFIED; |
| 819 | } |
| 820 | #endif |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 821 | timeval socket_timeout = TimeDeltaToTimeVal(timeout); |
| 822 | setsockopt(socket.fd(), |
| 823 | SOL_SOCKET, |
| 824 | SO_SNDTIMEO, |
| 825 | &socket_timeout, |
| 826 | sizeof(socket_timeout)); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 827 | |
| 828 | // Found another process, prepare our command line |
[email protected] | 52db4aa | 2009-05-21 18:41:02 | [diff] [blame] | 829 | // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>". |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 830 | std::string to_send(kStartToken); |
| 831 | to_send.push_back(kTokenDelimiter); |
| 832 | |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 833 | base::FilePath current_dir; |
Avi Drissman | 9098f900 | 2018-05-04 00:11:52 | [diff] [blame] | 834 | if (!base::PathService::Get(base::DIR_CURRENT, ¤t_dir)) |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 835 | return PROCESS_NONE; |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 836 | to_send.append(current_dir.value()); |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 837 | |
[email protected] | 0189bbd | 2009-10-12 22:50:39 | [diff] [blame] | 838 | const std::vector<std::string>& argv = cmd_line.argv(); |
jdoerrie | 2f1af51 | 2018-10-03 00:59:37 | [diff] [blame] | 839 | for (auto it = argv.begin(); it != argv.end(); ++it) { |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 840 | to_send.push_back(kTokenDelimiter); |
[email protected] | 52db4aa | 2009-05-21 18:41:02 | [diff] [blame] | 841 | to_send.append(*it); |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 842 | } |
| 843 | |
[email protected] | b674dc73 | 2009-05-20 20:41:00 | [diff] [blame] | 844 | // Send the message |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 845 | if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 846 | // Try to kill the other process, because it might have been dead. |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 847 | if (!kill_unresponsive || !KillProcessByLockPath(true)) |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 848 | return PROFILE_IN_USE; |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 849 | SendRemoteHungProcessTerminateReasonHistogram(SOCKET_WRITE_FAILED); |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 850 | return PROCESS_NONE; |
[email protected] | dc609a17 | 2009-07-31 05:31:27 | [diff] [blame] | 851 | } |
| 852 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 853 | if (shutdown(socket.fd(), SHUT_WR) < 0) |
[email protected] | 57b76567 | 2009-10-13 18:27:40 | [diff] [blame] | 854 | PLOG(ERROR) << "shutdown() failed"; |
[email protected] | dc609a17 | 2009-07-31 05:31:27 | [diff] [blame] | 855 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 856 | // Read ACK message from the other process. It might be blocked for a certain |
| 857 | // timeout, to make sure the other process has enough time to return ACK. |
| 858 | char buf[kMaxACKMessageLength + 1]; |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 859 | ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 860 | |
| 861 | // Failed to read ACK, the other process might have been frozen. |
| 862 | if (len <= 0) { |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 863 | if (!kill_unresponsive || !KillProcessByLockPath(true)) |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 864 | return PROFILE_IN_USE; |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 865 | SendRemoteHungProcessTerminateReasonHistogram(SOCKET_READ_FAILED); |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 866 | return PROCESS_NONE; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 867 | } |
| 868 | |
| 869 | buf[len] = '\0'; |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 870 | if (strncmp(buf, kShutdownToken, base::size(kShutdownToken) - 1) == 0) { |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 871 | // The other process is shutting down, it's safe to start a new process. |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 872 | SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_SHUTTING_DOWN); |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 873 | return PROCESS_NONE; |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 874 | } else if (strncmp(buf, kACKToken, base::size(kACKToken) - 1) == 0) { |
[email protected] | 1abeec3 | 2014-07-16 05:56:11 | [diff] [blame] | 875 | #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS) |
[email protected] | cc04a15 | 2014-02-12 09:17:16 | [diff] [blame] | 876 | // Likely NULL in unit tests. |
| 877 | views::LinuxUI* linux_ui = views::LinuxUI::instance(); |
| 878 | if (linux_ui) |
| 879 | linux_ui->NotifyWindowManagerStartupComplete(); |
| 880 | #endif |
| 881 | |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 882 | // Assume the other process is handling the request. |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 883 | return PROCESS_NOTIFIED; |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 884 | } |
| 885 | |
| 886 | NOTREACHED() << "The other process returned unknown message: " << buf; |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 887 | return PROCESS_NOTIFIED; |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 888 | } |
| 889 | |
[email protected] | dd85d45 | 2013-03-28 12:39:59 | [diff] [blame] | 890 | ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() { |
[email protected] | 4a44bc3 | 2010-05-28 22:22:44 | [diff] [blame] | 891 | return NotifyOtherProcessWithTimeoutOrCreate( |
avi | 556c0502 | 2014-12-22 23:31:43 | [diff] [blame] | 892 | *base::CommandLine::ForCurrentProcess(), kRetryAttempts, |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 893 | base::TimeDelta::FromSeconds(kTimeoutInSeconds)); |
[email protected] | 4a44bc3 | 2010-05-28 22:22:44 | [diff] [blame] | 894 | } |
| 895 | |
| 896 | ProcessSingleton::NotifyResult |
| 897 | ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate( |
avi | 556c0502 | 2014-12-22 23:31:43 | [diff] [blame] | 898 | const base::CommandLine& command_line, |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 899 | int retry_attempts, |
| 900 | const base::TimeDelta& timeout) { |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 901 | const base::TimeTicks begin_ticks = base::TimeTicks::Now(); |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 902 | NotifyResult result = NotifyOtherProcessWithTimeout( |
| 903 | command_line, retry_attempts, timeout, true); |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 904 | if (result != PROCESS_NONE) { |
| 905 | if (result == PROCESS_NOTIFIED) { |
| 906 | UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToNotify", |
| 907 | base::TimeTicks::Now() - begin_ticks); |
| 908 | } else { |
| 909 | UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToFailure", |
| 910 | base::TimeTicks::Now() - begin_ticks); |
| 911 | } |
[email protected] | 4a44bc3 | 2010-05-28 22:22:44 | [diff] [blame] | 912 | return result; |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 913 | } |
| 914 | |
| 915 | if (Create()) { |
| 916 | UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToCreate", |
| 917 | base::TimeTicks::Now() - begin_ticks); |
[email protected] | 4a44bc3 | 2010-05-28 22:22:44 | [diff] [blame] | 918 | return PROCESS_NONE; |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 919 | } |
| 920 | |
[email protected] | 4a44bc3 | 2010-05-28 22:22:44 | [diff] [blame] | 921 | // If the Create() failed, try again to notify. (It could be that another |
| 922 | // instance was starting at the same time and managed to grab the lock before |
| 923 | // we did.) |
| 924 | // This time, we don't want to kill anything if we aren't successful, since we |
| 925 | // aren't going to try to take over the lock ourselves. |
mattm | a92250e | 2014-09-09 07:26:16 | [diff] [blame] | 926 | result = NotifyOtherProcessWithTimeout( |
| 927 | command_line, retry_attempts, timeout, false); |
gab | 439f54f | 2016-10-07 22:24:22 | [diff] [blame] | 928 | |
| 929 | if (result == PROCESS_NOTIFIED) { |
| 930 | UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToNotify", |
| 931 | base::TimeTicks::Now() - begin_ticks); |
| 932 | } else { |
| 933 | UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToFailure", |
| 934 | base::TimeTicks::Now() - begin_ticks); |
| 935 | } |
| 936 | |
[email protected] | 4a44bc3 | 2010-05-28 22:22:44 | [diff] [blame] | 937 | if (result != PROCESS_NONE) |
| 938 | return result; |
| 939 | |
| 940 | return LOCK_ERROR; |
| 941 | } |
| 942 | |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 943 | void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) { |
| 944 | current_pid_ = pid; |
| 945 | } |
| 946 | |
| 947 | void ProcessSingleton::OverrideKillCallbackForTesting( |
| 948 | const base::Callback<void(int)>& callback) { |
| 949 | kill_callback_ = callback; |
| 950 | } |
| 951 | |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 952 | // static |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 953 | void ProcessSingleton::DisablePromptForTesting() { |
| 954 | g_disable_prompt = true; |
| 955 | } |
| 956 | |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 957 | // static |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 958 | void ProcessSingleton::SkipIsChromeProcessCheckForTesting(bool skip) { |
| 959 | g_skip_is_chrome_process_check = skip; |
| 960 | } |
| 961 | |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 962 | // static |
| 963 | void ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting( |
| 964 | bool set_unlock) { |
| 965 | g_user_opted_unlock_in_use_profile = set_unlock; |
| 966 | } |
| 967 | |
[email protected] | dd85d45 | 2013-03-28 12:39:59 | [diff] [blame] | 968 | bool ProcessSingleton::Create() { |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 969 | int sock; |
| 970 | sockaddr_un addr; |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 971 | |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 972 | // The symlink lock is pointed to the hostname and process id, so other |
| 973 | // processes can find it out. |
Christopher Cameron | 670c4d9 | 2019-07-29 18:39:38 | [diff] [blame] | 974 | base::FilePath symlink_content( |
| 975 | base::StringPrintf("%s%c%u", net::GetHostName().c_str(), |
| 976 | kProcessSingletonLockDelimiter, current_pid_)); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 977 | |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 978 | // Create symbol link before binding the socket, to ensure only one instance |
| 979 | // can have the socket open. |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 980 | if (!SymlinkPath(symlink_content, lock_path_)) { |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 981 | // TODO(jackhou): Remove this case once this code is stable on Mac. |
| 982 | // http://crbug.com/367612 |
| 983 | #if defined(OS_MACOSX) |
| 984 | // On Mac, an existing non-symlink lock file means the lock could be held by |
| 985 | // the old process singleton code. If we can successfully replace the lock, |
| 986 | // continue as normal. |
| 987 | if (base::IsLink(lock_path_) || |
| 988 | !ReplaceOldSingletonLock(symlink_content, lock_path_)) { |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 989 | return false; |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 990 | } |
| 991 | #else |
| 992 | // If we failed to create the lock, most likely another instance won the |
| 993 | // startup race. |
| 994 | return false; |
| 995 | #endif |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 996 | } |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 997 | |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 998 | // Create the socket file somewhere in /tmp which is usually mounted as a |
| 999 | // normal filesystem. Some network filesystems (notably AFS) are screwy and |
| 1000 | // do not support Unix domain sockets. |
| 1001 | if (!socket_dir_.CreateUniqueTempDir()) { |
| 1002 | LOG(ERROR) << "Failed to create socket directory."; |
| 1003 | return false; |
| 1004 | } |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 1005 | |
| 1006 | // Check that the directory was created with the correct permissions. |
| 1007 | int dir_mode = 0; |
vabr | 8023d87 | 2016-09-15 08:12:22 | [diff] [blame] | 1008 | CHECK(base::GetPosixFilePermissions(socket_dir_.GetPath(), &dir_mode) && |
[email protected] | c17ecbe | 2014-05-01 10:50:05 | [diff] [blame] | 1009 | dir_mode == base::FILE_PERMISSION_USER_MASK) |
| 1010 | << "Temp directory mode is not 700: " << std::oct << dir_mode; |
| 1011 | |
mattm | 916edf16 | 2017-04-13 21:26:51 | [diff] [blame] | 1012 | // Try to create the socket before creating the symlink, as SetupSocket may |
| 1013 | // fail on a CHECK if the |socket_target_path| is too long, and this avoids |
| 1014 | // leaving a dangling symlink. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 1015 | base::FilePath socket_target_path = |
vabr | 8023d87 | 2016-09-15 08:12:22 | [diff] [blame] | 1016 | socket_dir_.GetPath().Append(chrome::kSingletonSocketFilename); |
mattm | 916edf16 | 2017-04-13 21:26:51 | [diff] [blame] | 1017 | SetupSocket(socket_target_path.value(), &sock, &addr); |
| 1018 | |
| 1019 | // Setup the socket symlink and the two cookies. |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 1020 | base::FilePath cookie(GenerateCookie()); |
| 1021 | base::FilePath remote_cookie_path = |
vabr | 8023d87 | 2016-09-15 08:12:22 | [diff] [blame] | 1022 | socket_dir_.GetPath().Append(chrome::kSingletonCookieFilename); |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 1023 | UnlinkPath(socket_path_); |
| 1024 | UnlinkPath(cookie_path_); |
| 1025 | if (!SymlinkPath(socket_target_path, socket_path_) || |
| 1026 | !SymlinkPath(cookie, cookie_path_) || |
| 1027 | !SymlinkPath(cookie, remote_cookie_path)) { |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 1028 | // We've already locked things, so we can't have lost the startup race, |
| 1029 | // but something doesn't like us. |
| 1030 | LOG(ERROR) << "Failed to create symlinks."; |
[email protected] | 2d57f5d | 2011-01-13 14:20:12 | [diff] [blame] | 1031 | if (!socket_dir_.Delete()) |
| 1032 | LOG(ERROR) << "Encountered a problem when deleting socket directory."; |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 1033 | return false; |
| 1034 | } |
| 1035 | |
[email protected] | ac39c52 | 2009-06-24 21:36:17 | [diff] [blame] | 1036 | if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) { |
[email protected] | 53f4826c | 2010-08-27 01:29:28 | [diff] [blame] | 1037 | PLOG(ERROR) << "Failed to bind() " << socket_target_path.value(); |
[email protected] | 8b08cbd | 2009-08-04 05:34:19 | [diff] [blame] | 1038 | CloseSocket(sock); |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 1039 | return false; |
[email protected] | ac39c52 | 2009-06-24 21:36:17 | [diff] [blame] | 1040 | } |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 1041 | |
| 1042 | if (listen(sock, 5) < 0) |
brettw | 6ee6fd6 | 2015-06-09 18:05:24 | [diff] [blame] | 1043 | NOTREACHED() << "listen failed: " << base::safe_strerror(errno); |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 1044 | |
Gabriel Charette | 2983181c | 2018-03-28 17:01:09 | [diff] [blame] | 1045 | DCHECK(BrowserThread::IsThreadInitialized(BrowserThread::IO)); |
Sami Kyostila | 7d640eb | 2019-07-31 18:50:26 | [diff] [blame] | 1046 | base::PostTask(FROM_HERE, {BrowserThread::IO}, |
| 1047 | base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening, |
| 1048 | watcher_, sock)); |
[email protected] | 4dd4224 | 2010-04-07 02:21:15 | [diff] [blame] | 1049 | |
| 1050 | return true; |
[email protected] | 19d7e968 | 2009-02-18 22:04:28 | [diff] [blame] | 1051 | } |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 1052 | |
| 1053 | void ProcessSingleton::Cleanup() { |
[email protected] | 723571a | 2010-12-03 17:37:54 | [diff] [blame] | 1054 | UnlinkPath(socket_path_); |
| 1055 | UnlinkPath(cookie_path_); |
| 1056 | UnlinkPath(lock_path_); |
[email protected] | 9f20a6d0 | 2009-08-21 01:18:37 | [diff] [blame] | 1057 | } |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1058 | |
| 1059 | bool ProcessSingleton::IsSameChromeInstance(pid_t pid) { |
| 1060 | pid_t cur_pid = current_pid_; |
| 1061 | while (pid != cur_pid) { |
| 1062 | pid = base::GetParentProcessId(pid); |
Matt Mueller | 65ef3a7 | 2017-08-04 15:25:16 | [diff] [blame] | 1063 | if (pid <= 0) |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1064 | return false; |
| 1065 | if (!IsChromeProcess(pid)) |
| 1066 | return false; |
| 1067 | } |
| 1068 | return true; |
| 1069 | } |
| 1070 | |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 1071 | bool ProcessSingleton::KillProcessByLockPath(bool is_connected_to_socket) { |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1072 | std::string hostname; |
| 1073 | int pid; |
Christopher Cameron | 670c4d9 | 2019-07-29 18:39:38 | [diff] [blame] | 1074 | ParseProcessSingletonLock(lock_path_, &hostname, &pid); |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1075 | |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 1076 | if (!hostname.empty() && hostname != net::GetHostName() && |
| 1077 | !is_connected_to_socket) { |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 1078 | bool res = DisplayProfileInUseError(lock_path_, hostname, pid); |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 1079 | if (res) { |
| 1080 | UnlinkPath(lock_path_); |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 1081 | SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED_BEFORE_KILL); |
aseren | 67095448 | 2017-06-06 18:14:12 | [diff] [blame] | 1082 | } |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 1083 | return res; |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1084 | } |
| 1085 | UnlinkPath(lock_path_); |
| 1086 | |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 1087 | if (IsSameChromeInstance(pid)) { |
| 1088 | SendRemoteProcessInteractionResultHistogram( |
| 1089 | SAME_BROWSER_INSTANCE_BEFORE_KILL); |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1090 | return true; |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 1091 | } |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1092 | |
| 1093 | if (pid > 0) { |
| 1094 | kill_callback_.Run(pid); |
| 1095 | return true; |
| 1096 | } |
| 1097 | |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 1098 | SendRemoteProcessInteractionResultHistogram(FAILED_TO_EXTRACT_PID); |
| 1099 | |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1100 | LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value(); |
| 1101 | return true; |
| 1102 | } |
| 1103 | |
| 1104 | void ProcessSingleton::KillProcess(int pid) { |
| 1105 | // TODO([email protected]): Is SIGKILL ok? |
| 1106 | int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL); |
| 1107 | // ESRCH = No Such Process (can happen if the other process is already in |
| 1108 | // progress of shutting down and finishes before we try to kill it). |
| 1109 | DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: " |
brettw | 6ee6fd6 | 2015-06-09 18:05:24 | [diff] [blame] | 1110 | << base::safe_strerror(errno); |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 1111 | |
| 1112 | int error_code = (rv == 0) ? 0 : errno; |
Ilya Sherman | 982457e6 | 2017-12-13 02:19:36 | [diff] [blame] | 1113 | base::UmaHistogramSparse( |
aseren | 028ea15 | 2017-05-16 17:22:43 | [diff] [blame] | 1114 | "Chrome.ProcessSingleton.TerminateProcessErrorCode.Posix", error_code); |
| 1115 | |
| 1116 | RemoteProcessInteractionResult action = TERMINATE_SUCCEEDED; |
| 1117 | if (rv != 0) { |
| 1118 | switch (error_code) { |
| 1119 | case ESRCH: |
| 1120 | action = REMOTE_PROCESS_NOT_FOUND; |
| 1121 | break; |
| 1122 | case EPERM: |
| 1123 | action = TERMINATE_NOT_ENOUGH_PERMISSIONS; |
| 1124 | break; |
| 1125 | default: |
| 1126 | action = TERMINATE_FAILED; |
| 1127 | break; |
| 1128 | } |
| 1129 | } |
| 1130 | SendRemoteProcessInteractionResultHistogram(action); |
[email protected] | 65718d9 | 2012-05-02 23:02:58 | [diff] [blame] | 1131 | } |