blob: 714ac65175c2461a9f6def3b3cf891af2b32826f [file] [log] [blame]
[email protected]c17ecbe2014-05-01 10:50:051// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]19d7e9682009-02-18 22:04:282// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]b674dc732009-05-20 20:41:005// 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]8b08cbd2009-08-04 05:34:1910//
[email protected]53f4826c2010-08-27 01:29:2811// 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]9f20a6d02009-08-21 01:18:3721// 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]8b08cbd2009-08-04 05:34:1926//
[email protected]9f20a6d02009-08-21 01:18:3727// 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]8b08cbd2009-08-04 05:34:1932//
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]b674dc732009-05-20 20:41:0039
[email protected]19d7e9682009-02-18 22:04:2840#include "chrome/browser/process_singleton.h"
41
42#include <errno.h>
[email protected]8e97eb2d2009-05-22 23:01:0243#include <fcntl.h>
[email protected]8b08cbd2009-08-04 05:34:1944#include <signal.h>
[email protected]86378072009-07-31 07:09:3445#include <sys/socket.h>
[email protected]8b08cbd2009-08-04 05:34:1946#include <sys/stat.h>
47#include <sys/types.h>
[email protected]19d7e9682009-02-18 22:04:2848#include <sys/un.h>
[email protected]8b08cbd2009-08-04 05:34:1949#include <unistd.h>
[email protected]93270d002011-01-19 22:32:5950
[email protected]8b08cbd2009-08-04 05:34:1951#include <cstring>
fdoray00285d72016-10-14 15:14:2052#include <memory>
[email protected]8e97eb2d2009-05-22 23:01:0253#include <set>
[email protected]8b08cbd2009-08-04 05:34:1954#include <string>
[email protected]19d7e9682009-02-18 22:04:2855
avib896c712015-12-26 02:10:4356#include <stddef.h>
57
[email protected]b674dc732009-05-20 20:41:0058#include "base/base_paths.h"
[email protected]4b3fe63c2011-11-17 00:53:0359#include "base/bind.h"
[email protected]b674dc732009-05-20 20:41:0060#include "base/command_line.h"
David Benjamind55612d2019-04-02 23:33:0561#include "base/containers/unique_ptr_adapters.h"
fdoray00285d72016-10-14 15:14:2062#include "base/files/file_descriptor_watcher_posix.h"
[email protected]57999812013-02-24 05:40:5263#include "base/files/file_path.h"
thestig18dfb7a52014-08-26 10:44:0464#include "base/files/file_util.h"
skyostil02598352015-06-12 12:37:2565#include "base/location.h"
[email protected]19d7e9682009-02-18 22:04:2866#include "base/logging.h"
fdorayc16c6f82016-06-29 15:27:3267#include "base/memory/ref_counted.h"
Ilya Sherman982457e62017-12-13 02:19:3668#include "base/metrics/histogram_functions.h"
gab439f54f2016-10-07 22:24:2269#include "base/metrics/histogram_macros.h"
[email protected]b674dc732009-05-20 20:41:0070#include "base/path_service.h"
[email protected]2025d002012-11-14 20:54:3571#include "base/posix/eintr_wrapper.h"
brettw6ee6fd62015-06-09 18:05:2472#include "base/posix/safe_strerror.h"
[email protected]53f4826c2010-08-27 01:29:2873#include "base/rand_util.h"
[email protected]fb441962013-05-08 05:35:2474#include "base/sequenced_task_runner_helpers.h"
skyostil02598352015-06-12 12:37:2575#include "base/single_thread_task_runner.h"
Avi Drissman5f0fb8c2018-12-25 23:20:4976#include "base/stl_util.h"
[email protected]3ea1b182013-02-08 22:38:4177#include "base/strings/string_number_conversions.h"
[email protected]1988e1c2013-02-28 20:27:4278#include "base/strings/string_split.h"
[email protected]539f6b32014-08-12 02:50:0079#include "base/strings/string_util.h"
[email protected]76fb05c2013-06-11 04:38:0580#include "base/strings/stringprintf.h"
[email protected]3268d7b72013-03-28 17:41:4381#include "base/strings/sys_string_conversions.h"
[email protected]e309f312013-06-07 21:50:0882#include "base/strings/utf_string_conversions.h"
Eric Seckler8652dcd52018-09-20 10:42:2883#include "base/task/post_task.h"
[email protected]f214f8792011-01-01 02:17:0884#include "base/threading/platform_thread.h"
fdorayc16c6f82016-06-29 15:27:3285#include "base/threading/thread_task_runner_handle.h"
[email protected]84813472013-06-28 00:25:1986#include "base/time/time.h"
87#include "base/timer/timer.h"
avib896c712015-12-26 02:10:4388#include "build/build_config.h"
[email protected]1912cfe2009-04-21 08:09:3089#include "chrome/common/chrome_constants.h"
Christopher Cameron670c4d92019-07-29 18:39:3890#include "chrome/common/process_singleton_lock_posix.h"
[email protected]af39f002014-08-22 10:18:1891#include "chrome/grit/chromium_strings.h"
92#include "chrome/grit/generated_resources.h"
Eric Seckler8652dcd52018-09-20 10:42:2893#include "content/public/browser/browser_task_traits.h"
[email protected]c38831a12011-10-28 12:44:4994#include "content/public/browser/browser_thread.h"
tfarinae5f66e62016-02-12 02:48:4095#include "net/base/network_interfaces.h"
[email protected]c051a1b2011-01-21 23:30:1796#include "ui/base/l10n/l10n_util.h"
[email protected]19d7e9682009-02-18 22:04:2897
[email protected]c17ecbe2014-05-01 10:50:0598#if defined(OS_LINUX)
99#include "chrome/browser/ui/process_singleton_dialog_linux.h"
100#endif
101
[email protected]1abeec32014-07-16 05:56:11102#if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
[email protected]cc04a152014-02-12 09:17:16103#include "ui/views/linux_ui/linux_ui.h"
104#endif
105
[email protected]631bb742011-11-02 11:29:39106using content::BrowserThread;
107
[email protected]b674dc732009-05-20 20:41:00108namespace {
[email protected]8e97eb2d2009-05-22 23:01:02109
mattma92250e2014-09-09 07:26:16110// Timeout for the current browser process to respond. 20 seconds should be
111// enough.
112const int kTimeoutInSeconds = 20;
113// Number of retries to notify the browser. 20 retries over 20 seconds = 1 try
114// per second.
115const int kRetryAttempts = 20;
[email protected]8e97eb2d2009-05-22 23:01:02116const char kStartToken[] = "START";
[email protected]8b08cbd2009-08-04 05:34:19117const char kACKToken[] = "ACK";
118const char kShutdownToken[] = "SHUTDOWN";
[email protected]b674dc732009-05-20 20:41:00119const char kTokenDelimiter = '\0';
[email protected]8e97eb2d2009-05-22 23:01:02120const int kMaxMessageLength = 32 * 1024;
Avi Drissman5f0fb8c2018-12-25 23:20:49121const int kMaxACKMessageLength = base::size(kShutdownToken) - 1;
[email protected]8e97eb2d2009-05-22 23:01:02122
aseren028ea152017-05-16 17:22:43123bool g_disable_prompt = false;
124bool g_skip_is_chrome_process_check = false;
aseren670954482017-06-06 18:14:12125bool g_user_opted_unlock_in_use_profile = false;
aseren028ea152017-05-16 17:22:43126
[email protected]becc0bb2009-08-03 17:29:49127// Set the close-on-exec bit on a file descriptor.
128// Returns 0 on success, -1 on failure.
129int 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]8b08cbd2009-08-04 05:34:19138// Close a socket and check return value.
139void CloseSocket(int fd) {
[email protected]d89eec82013-12-03 14:10:59140 int rv = IGNORE_EINTR(close(fd));
brettw6ee6fd62015-06-09 18:05:24141 DCHECK_EQ(0, rv) << "Error closing socket: " << base::safe_strerror(errno);
[email protected]8b08cbd2009-08-04 05:34:19142}
143
144// Write a message to a socket fd.
145bool 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]57b765672009-10-13 18:27:40159 PLOG(ERROR) << "write() failed";
[email protected]8b08cbd2009-08-04 05:34:19160 return false;
161 }
162 bytes_written += rv;
163 } while (bytes_written < length);
164
165 return true;
166}
167
mattma92250e2014-09-09 07:26:16168struct 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]8b08cbd2009-08-04 05:34:19176// Returns -1 if error occurred, 0 if timeout reached, > 0 if the socket is
177// ready for read.
mattma92250e2014-09-09 07:26:16178int WaitSocketForRead(int fd, const base::TimeDelta& timeout) {
[email protected]8b08cbd2009-08-04 05:34:19179 fd_set read_fds;
mattma92250e2014-09-09 07:26:16180 struct timeval tv = TimeDeltaToTimeVal(timeout);
[email protected]8b08cbd2009-08-04 05:34:19181
182 FD_ZERO(&read_fds);
183 FD_SET(fd, &read_fds);
[email protected]8b08cbd2009-08-04 05:34:19184
185 return HANDLE_EINTR(select(fd + 1, &read_fds, NULL, NULL, &tv));
186}
187
mattma92250e2014-09-09 07:26:16188// Read a message from a socket fd, with an optional timeout.
[email protected]8b08cbd2009-08-04 05:34:19189// If |timeout| <= 0 then read immediately.
190// Return number of bytes actually read, or -1 on error.
mattma92250e2014-09-09 07:26:16191ssize_t ReadFromSocket(int fd,
192 char* buf,
193 size_t bufsize,
194 const base::TimeDelta& timeout) {
195 if (timeout > base::TimeDelta()) {
[email protected]8b08cbd2009-08-04 05:34:19196 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]57b765672009-10-13 18:27:40206 PLOG(ERROR) << "read() failed";
[email protected]8b08cbd2009-08-04 05:34:19207 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]53f4826c2010-08-27 01:29:28223// Set up a sockaddr appropriate for messaging.
mattm916edf162017-04-13 21:26:51224bool SetupSockAddr(const std::string& path, struct sockaddr_un* addr) {
[email protected]8b08cbd2009-08-04 05:34:19225 addr->sun_family = AF_UNIX;
Avi Drissman5f0fb8c2018-12-25 23:20:49226 if (path.length() >= base::size(addr->sun_path))
mattm916edf162017-04-13 21:26:51227 return false;
Avi Drissman5f0fb8c2018-12-25 23:20:49228 base::strlcpy(addr->sun_path, path.c_str(), base::size(addr->sun_path));
mattm916edf162017-04-13 21:26:51229 return true;
[email protected]8b08cbd2009-08-04 05:34:19230}
231
[email protected]53f4826c2010-08-27 01:29:28232// Set up a socket appropriate for messaging.
233int SetupSocketOnly() {
234 int sock = socket(PF_UNIX, SOCK_STREAM, 0);
235 PCHECK(sock >= 0) << "socket() failed";
236
tfarina060df7e2015-12-16 05:15:32237 DCHECK(base::SetNonBlocking(sock)) << "Failed to make non-blocking socket.";
238 int rv = SetCloseOnExec(sock);
[email protected]53f4826c2010-08-27 01:29:28239 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.
245void SetupSocket(const std::string& path, int* sock, struct sockaddr_un* addr) {
246 *sock = SetupSocketOnly();
mattm916edf162017-04-13 21:26:51247 CHECK(SetupSockAddr(path, addr)) << "Socket path too long: " << path;
[email protected]53f4826c2010-08-27 01:29:28248}
249
[email protected]53f4826c2010-08-27 01:29:28250// Read a symbolic link, return empty string if given path is not a symbol link.
[email protected]650b2d52013-02-10 03:41:45251base::FilePath ReadLink(const base::FilePath& path) {
252 base::FilePath target;
[email protected]b264eab2013-11-27 23:22:08253 if (!base::ReadSymbolicLink(path, &target)) {
[email protected]53f4826c2010-08-27 01:29:28254 // The only errno that should occur is ENOENT.
255 if (errno != 0 && errno != ENOENT)
[email protected]723571a2010-12-03 17:37:54256 PLOG(ERROR) << "readlink(" << path.value() << ") failed";
[email protected]8b08cbd2009-08-04 05:34:19257 }
[email protected]53f4826c2010-08-27 01:29:28258 return target;
[email protected]8b08cbd2009-08-04 05:34:19259}
260
[email protected]9f20a6d02009-08-21 01:18:37261// Unlink a path. Return true on success.
[email protected]650b2d52013-02-10 03:41:45262bool UnlinkPath(const base::FilePath& path) {
[email protected]723571a2010-12-03 17:37:54263 int rv = unlink(path.value().c_str());
[email protected]314667782009-09-14 22:34:42264 if (rv < 0 && errno != ENOENT)
[email protected]723571a2010-12-03 17:37:54265 PLOG(ERROR) << "Failed to unlink " << path.value();
[email protected]8b08cbd2009-08-04 05:34:19266
[email protected]9f20a6d02009-08-21 01:18:37267 return rv == 0;
[email protected]8b08cbd2009-08-04 05:34:19268}
269
[email protected]53f4826c2010-08-27 01:29:28270// Create a symlink. Returns true on success.
[email protected]650b2d52013-02-10 03:41:45271bool SymlinkPath(const base::FilePath& target, const base::FilePath& path) {
[email protected]b264eab2013-11-27 23:22:08272 if (!base::CreateSymbolicLink(target, path)) {
[email protected]53f4826c2010-08-27 01:29:28273 // 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]723571a2010-12-03 17:37:54280 PLOG(ERROR) << "Failed to create " << path.value();
[email protected]53f4826c2010-08-27 01:29:28281 return false;
282 }
283 }
284 return true;
285}
286
[email protected]37248d72013-09-11 04:31:27287// Returns true if the user opted to unlock the profile.
288bool DisplayProfileInUseError(const base::FilePath& lock_path,
[email protected]9f20a6d02009-08-21 01:18:37289 const std::string& hostname,
290 int pid) {
[email protected]0085863a2013-12-06 21:19:03291 base::string16 error = l10n_util::GetStringFUTF16(
Raul Tambrefff51b752019-02-04 13:09:47292 IDS_PROFILE_IN_USE_POSIX, base::NumberToString16(pid),
[email protected]6778fed2013-12-24 20:09:37293 base::ASCIIToUTF16(hostname));
[email protected]c17ecbe2014-05-01 10:50:05294 LOG(ERROR) << error;
295
296 if (g_disable_prompt)
aseren670954482017-06-06 18:14:12297 return g_user_opted_unlock_in_use_profile;
[email protected]c17ecbe2014-05-01 10:50:05298
299#if defined(OS_LINUX)
[email protected]0085863a2013-12-06 21:19:03300 base::string16 relaunch_button_text = l10n_util::GetStringUTF16(
[email protected]37248d72013-09-11 04:31:27301 IDS_PROFILE_IN_USE_LINUX_RELAUNCH);
[email protected]c17ecbe2014-05-01 10:50:05302 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]37248d72013-09-11 04:31:27309 return false;
[email protected]9f20a6d02009-08-21 01:18:37310}
311
[email protected]a70d9cf2010-05-11 23:05:19312bool IsChromeProcess(pid_t pid) {
aseren670954482017-06-06 18:14:12313 if (g_skip_is_chrome_process_check)
314 return true;
315
[email protected]650b2d52013-02-10 03:41:45316 base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid));
[email protected]a70d9cf2010-05-11 23:05:19317 return (!other_chrome_path.empty() &&
aseren670954482017-06-06 18:14:12318 other_chrome_path.BaseName() ==
319 base::FilePath(chrome::kBrowserProcessExecutableName));
[email protected]a70d9cf2010-05-11 23:05:19320}
321
[email protected]53f4826c2010-08-27 01:29:28322// A helper class to hold onto a socket.
323class ScopedSocket {
[email protected]8b08cbd2009-08-04 05:34:19324 public:
[email protected]53f4826c2010-08-27 01:29:28325 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]8b08cbd2009-08-04 05:34:19337 private:
338 int fd_;
339};
340
[email protected]53f4826c2010-08-27 01:29:28341// Returns a random string for uniquifying profile connections.
342std::string GenerateCookie() {
Daniel Cheng3d199b12017-12-12 03:51:09343 return base::NumberToString(base::RandUint64());
[email protected]53f4826c2010-08-27 01:29:28344}
345
[email protected]650b2d52013-02-10 03:41:45346bool CheckCookie(const base::FilePath& path, const base::FilePath& cookie) {
[email protected]723571a2010-12-03 17:37:54347 return (cookie == ReadLink(path));
[email protected]53f4826c2010-08-27 01:29:28348}
349
350bool ConnectSocket(ScopedSocket* socket,
[email protected]650b2d52013-02-10 03:41:45351 const base::FilePath& socket_path,
352 const base::FilePath& cookie_path) {
353 base::FilePath socket_target;
[email protected]b264eab2013-11-27 23:22:08354 if (base::ReadSymbolicLink(socket_path, &socket_target)) {
[email protected]53f4826c2010-08-27 01:29:28355 // It's a symlink. Read the cookie.
[email protected]650b2d52013-02-10 03:41:45356 base::FilePath cookie = ReadLink(cookie_path);
[email protected]53f4826c2010-08-27 01:29:28357 if (cookie.empty())
358 return false;
[email protected]650b2d52013-02-10 03:41:45359 base::FilePath remote_cookie = socket_target.DirName().
[email protected]723571a2010-12-03 17:37:54360 Append(chrome::kSingletonCookieFilename);
[email protected]53f4826c2010-08-27 01:29:28361 // 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;
mattm916edf162017-04-13 21:26:51367 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]53f4826c2010-08-27 01:29:28373 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;
mattm916edf162017-04-13 21:26:51391 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]53f4826c2010-08-27 01:29:28397 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]c17ecbe2014-05-01 10:50:05409#if defined(OS_MACOSX)
410bool 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
aseren028ea152017-05-16 17:22:43444void SendRemoteProcessInteractionResultHistogram(
445 ProcessSingleton::RemoteProcessInteractionResult result) {
446 UMA_HISTOGRAM_ENUMERATION(
447 "Chrome.ProcessSingleton.RemoteProcessInteractionResult", result,
448 ProcessSingleton::REMOTE_PROCESS_INTERACTION_RESULT_COUNT);
449}
450
451void 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]8e97eb2d2009-05-22 23:01:02458} // namespace
459
[email protected]b674dc732009-05-20 20:41:00460///////////////////////////////////////////////////////////////////////////////
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.
465class ProcessSingleton::LinuxWatcher
fdoray00285d72016-10-14 15:14:20466 : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher,
[email protected]4b3fe63c2011-11-17 00:53:03467 BrowserThread::DeleteOnIOThread> {
[email protected]b674dc732009-05-20 20:41:00468 public:
[email protected]8b08cbd2009-08-04 05:34:19469 // A helper class to read message from an established socket.
fdoray00285d72016-10-14 15:14:20470 class SocketReader {
[email protected]8e97eb2d2009-05-22 23:01:02471 public:
472 SocketReader(ProcessSingleton::LinuxWatcher* parent,
fdorayc16c6f82016-06-29 15:27:32473 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
[email protected]8e97eb2d2009-05-22 23:01:02474 int fd)
475 : parent_(parent),
fdorayc16c6f82016-06-29 15:27:32476 ui_task_runner_(ui_task_runner),
[email protected]8e97eb2d2009-05-22 23:01:02477 fd_(fd),
478 bytes_read_(0) {
thestig00844cea2015-09-08 21:44:52479 DCHECK_CURRENTLY_ON(BrowserThread::IO);
[email protected]8e97eb2d2009-05-22 23:01:02480 // Wait for reads.
fdoray00285d72016-10-14 15:14:20481 fd_watch_controller_ = base::FileDescriptorWatcher::WatchReadable(
482 fd, base::Bind(&SocketReader::OnSocketCanReadWithoutBlocking,
483 base::Unretained(this)));
[email protected]4b3fe63c2011-11-17 00:53:03484 // If we haven't completed in a reasonable amount of time, give up.
[email protected]d323a172011-09-02 18:23:02485 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeoutInSeconds),
[email protected]4b3fe63c2011-11-17 00:53:03486 this, &SocketReader::CleanupAndDeleteSelf);
[email protected]8e97eb2d2009-05-22 23:01:02487 }
488
fdoray00285d72016-10-14 15:14:20489 ~SocketReader() { CloseSocket(fd_); }
[email protected]8e97eb2d2009-05-22 23:01:02490
[email protected]8b08cbd2009-08-04 05:34:19491 // 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]8e97eb2d2009-05-22 23:01:02495 private:
fdoray00285d72016-10-14 15:14:20496 void OnSocketCanReadWithoutBlocking();
497
[email protected]4b3fe63c2011-11-17 00:53:03498 void CleanupAndDeleteSelf() {
thestig00844cea2015-09-08 21:44:52499 DCHECK_CURRENTLY_ON(BrowserThread::IO);
[email protected]4b3fe63c2011-11-17 00:53:03500
[email protected]8e97eb2d2009-05-22 23:01:02501 parent_->RemoveSocketReader(this);
502 // We're deleted beyond this point.
503 }
504
fdoray00285d72016-10-14 15:14:20505 // Controls watching |fd_|.
506 std::unique_ptr<base::FileDescriptorWatcher::Controller>
507 fd_watch_controller_;
[email protected]8e97eb2d2009-05-22 23:01:02508
509 // The ProcessSingleton::LinuxWatcher that owns us.
510 ProcessSingleton::LinuxWatcher* const parent_;
511
fdorayc16c6f82016-06-29 15:27:32512 // A reference to the UI task runner.
513 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
[email protected]8e97eb2d2009-05-22 23:01:02514
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
danakj8c3eb802015-09-24 07:53:00525 base::OneShotTimer timer_;
[email protected]8e97eb2d2009-05-22 23:01:02526
527 DISALLOW_COPY_AND_ASSIGN(SocketReader);
528 };
529
[email protected]8b08cbd2009-08-04 05:34:19530 // We expect to only be constructed on the UI thread.
531 explicit LinuxWatcher(ProcessSingleton* parent)
fdorayc16c6f82016-06-29 15:27:32532 : ui_task_runner_(base::ThreadTaskRunnerHandle::Get()), parent_(parent) {}
[email protected]8b08cbd2009-08-04 05:34:19533
[email protected]8b08cbd2009-08-04 05:34:19534 // 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]4b3fe63c2011-11-17 00:53:03543 SocketReader* reader);
[email protected]8b08cbd2009-08-04 05:34:19544
[email protected]8b08cbd2009-08-04 05:34:19545 private:
[email protected]4b3fe63c2011-11-17 00:53:03546 friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
[email protected]cff06c572012-01-02 20:03:05547 friend class base::DeleteHelper<ProcessSingleton::LinuxWatcher>;
[email protected]8de85a62009-11-06 08:32:17548
fdoray00285d72016-10-14 15:14:20549 ~LinuxWatcher() {
thestig00844cea2015-09-08 21:44:52550 DCHECK_CURRENTLY_ON(BrowserThread::IO);
[email protected]8de85a62009-11-06 08:32:17551 }
552
fdoray00285d72016-10-14 15:14:20553 void OnSocketCanReadWithoutBlocking(int socket);
554
[email protected]8e97eb2d2009-05-22 23:01:02555 // Removes and deletes the SocketReader.
556 void RemoveSocketReader(SocketReader* reader);
557
fdoray00285d72016-10-14 15:14:20558 std::unique_ptr<base::FileDescriptorWatcher::Controller> socket_watcher_;
[email protected]b674dc732009-05-20 20:41:00559
560 // A reference to the UI message loop (i.e., the message loop we were
561 // constructed on).
fdorayc16c6f82016-06-29 15:27:32562 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
[email protected]b674dc732009-05-20 20:41:00563
564 // The ProcessSingleton that owns us.
[email protected]8e97eb2d2009-05-22 23:01:02565 ProcessSingleton* const parent_;
[email protected]b674dc732009-05-20 20:41:00566
David Benjamind55612d2019-04-02 23:33:05567 std::set<std::unique_ptr<SocketReader>, base::UniquePtrComparator> readers_;
[email protected]b674dc732009-05-20 20:41:00568
569 DISALLOW_COPY_AND_ASSIGN(LinuxWatcher);
570};
571
fdoray00285d72016-10-14 15:14:20572void ProcessSingleton::LinuxWatcher::OnSocketCanReadWithoutBlocking(
573 int socket) {
thestig00844cea2015-09-08 21:44:52574 DCHECK_CURRENTLY_ON(BrowserThread::IO);
[email protected]b674dc732009-05-20 20:41:00575 // Accepting incoming client.
576 sockaddr_un from;
577 socklen_t from_len = sizeof(from);
fdoray00285d72016-10-14 15:14:20578 int connection_socket = HANDLE_EINTR(
579 accept(socket, reinterpret_cast<sockaddr*>(&from), &from_len));
[email protected]b674dc732009-05-20 20:41:00580 if (-1 == connection_socket) {
[email protected]57b765672009-10-13 18:27:40581 PLOG(ERROR) << "accept() failed";
[email protected]b674dc732009-05-20 20:41:00582 return;
583 }
tfarina060df7e2015-12-16 05:15:32584 DCHECK(base::SetNonBlocking(connection_socket))
585 << "Failed to make non-blocking socket.";
avid2c61e42016-10-25 00:46:57586 readers_.insert(
Jeremy Romanec48d7a2018-03-01 17:35:09587 std::make_unique<SocketReader>(this, ui_task_runner_, connection_socket));
[email protected]b674dc732009-05-20 20:41:00588}
589
[email protected]b7c5c4c2009-05-21 15:55:16590void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
thestig00844cea2015-09-08 21:44:52591 DCHECK_CURRENTLY_ON(BrowserThread::IO);
[email protected]b7c5c4c2009-05-21 15:55:16592 // Watch for client connections on this socket.
fdoray00285d72016-10-14 15:14:20593 socket_watcher_ = base::FileDescriptorWatcher::WatchReadable(
594 socket, base::Bind(&LinuxWatcher::OnSocketCanReadWithoutBlocking,
595 base::Unretained(this), socket));
[email protected]b7c5c4c2009-05-21 15:55:16596}
597
[email protected]8b08cbd2009-08-04 05:34:19598void ProcessSingleton::LinuxWatcher::HandleMessage(
599 const std::string& current_dir, const std::vector<std::string>& argv,
600 SocketReader* reader) {
fdorayc16c6f82016-06-29 15:27:32601 DCHECK(ui_task_runner_->BelongsToCurrentThread());
[email protected]8b08cbd2009-08-04 05:34:19602 DCHECK(reader);
[email protected]cebf31962009-10-14 20:16:23603
avi556c05022014-12-22 23:31:43604 if (parent_->notification_callback_.Run(base::CommandLine(argv),
[email protected]650b2d52013-02-10 03:41:45605 base::FilePath(current_dir))) {
[email protected]5d364542012-04-05 07:15:39606 // Send back "ACK" message to prevent the client process from starting up.
Avi Drissman5f0fb8c2018-12-25 23:20:49607 reader->FinishWithACK(kACKToken, base::size(kACKToken) - 1);
[email protected]5d364542012-04-05 07:15:39608 } else {
[email protected]b674dc732009-05-20 20:41:00609 LOG(WARNING) << "Not handling interprocess notification as browser"
610 " is shutting down";
[email protected]8b08cbd2009-08-04 05:34:19611 // Send back "SHUTDOWN" message, so that the client process can start up
612 // without killing this process.
Avi Drissman5f0fb8c2018-12-25 23:20:49613 reader->FinishWithACK(kShutdownToken, base::size(kShutdownToken) - 1);
[email protected]b674dc732009-05-20 20:41:00614 return;
615 }
[email protected]b674dc732009-05-20 20:41:00616}
617
[email protected]8e97eb2d2009-05-22 23:01:02618void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) {
thestig00844cea2015-09-08 21:44:52619 DCHECK_CURRENTLY_ON(BrowserThread::IO);
[email protected]8e97eb2d2009-05-22 23:01:02620 DCHECK(reader);
David Benjamind55612d2019-04-02 23:33:05621 auto it = readers_.find(reader);
avid2c61e42016-10-25 00:46:57622 readers_.erase(it);
[email protected]8e97eb2d2009-05-22 23:01:02623}
624
[email protected]b674dc732009-05-20 20:41:00625///////////////////////////////////////////////////////////////////////////////
626// ProcessSingleton::LinuxWatcher::SocketReader
627//
628
fdoray00285d72016-10-14 15:14:20629void ProcessSingleton::LinuxWatcher::SocketReader::
630 OnSocketCanReadWithoutBlocking() {
thestig00844cea2015-09-08 21:44:52631 DCHECK_CURRENTLY_ON(BrowserThread::IO);
[email protected]8e97eb2d2009-05-22 23:01:02632 while (bytes_read_ < sizeof(buf_)) {
fdoray00285d72016-10-14 15:14:20633 ssize_t rv =
634 HANDLE_EINTR(read(fd_, buf_ + bytes_read_, sizeof(buf_) - bytes_read_));
[email protected]8e97eb2d2009-05-22 23:01:02635 if (rv < 0) {
636 if (errno != EAGAIN && errno != EWOULDBLOCK) {
[email protected]57b765672009-10-13 18:27:40637 PLOG(ERROR) << "read() failed";
fdoray00285d72016-10-14 15:14:20638 CloseSocket(fd_);
[email protected]8e97eb2d2009-05-22 23:01:02639 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]b674dc732009-05-20 20:41:00651 }
652
[email protected]52db4aa2009-05-21 18:41:02653 // Validate the message. The shortest message is kStartToken\0x\0x
Avi Drissman5f0fb8c2018-12-25 23:20:49654 const size_t kMinMessageLength = base::size(kStartToken) + 4;
[email protected]8e97eb2d2009-05-22 23:01:02655 if (bytes_read_ < kMinMessageLength) {
656 buf_[bytes_read_] = 0;
657 LOG(ERROR) << "Invalid socket message (wrong length):" << buf_;
[email protected]4b3fe63c2011-11-17 00:53:03658 CleanupAndDeleteSelf();
[email protected]b674dc732009-05-20 20:41:00659 return;
660 }
661
[email protected]8e97eb2d2009-05-22 23:01:02662 std::string str(buf_, bytes_read_);
brettwc6f82b12015-07-21 21:37:38663 std::vector<std::string> tokens = base::SplitString(
664 str, std::string(1, kTokenDelimiter),
665 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
[email protected]b674dc732009-05-20 20:41:00666
667 if (tokens.size() < 3 || tokens[0] != kStartToken) {
668 LOG(ERROR) << "Wrong message format: " << str;
[email protected]4b3fe63c2011-11-17 00:53:03669 CleanupAndDeleteSelf();
[email protected]b674dc732009-05-20 20:41:00670 return;
671 }
672
[email protected]8b08cbd2009-08-04 05:34:19673 // Stop the expiration timer to prevent this SocketReader object from being
674 // terminated unexpectly.
675 timer_.Stop();
676
[email protected]b674dc732009-05-20 20:41:00677 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.
fdorayc16c6f82016-06-29 15:27:32684 ui_task_runner_->PostTask(
tzik3f7781d2017-04-20 17:09:33685 FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
686 parent_, current_dir, tokens, this));
fdoray00285d72016-10-14 15:14:20687 fd_watch_controller_.reset();
[email protected]8e97eb2d2009-05-22 23:01:02688
[email protected]8b08cbd2009-08-04 05:34:19689 // LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
690 // object by invoking SocketReader::FinishWithACK().
691}
692
693void 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]57b765672009-10-13 18:27:40701 PLOG(ERROR) << "shutdown() failed";
[email protected]8b08cbd2009-08-04 05:34:19702
Sami Kyostila7d640eb2019-07-31 18:50:26703 base::PostTask(
Eric Seckler8652dcd52018-09-20 10:42:28704 FROM_HERE, {BrowserThread::IO},
tzik3f7781d2017-04-20 17:09:33705 base::BindOnce(&ProcessSingleton::LinuxWatcher::RemoveSocketReader,
706 parent_, this));
[email protected]4b3fe63c2011-11-17 00:53:03707 // We will be deleted once the posted RemoveSocketReader task runs.
[email protected]b674dc732009-05-20 20:41:00708}
709
710///////////////////////////////////////////////////////////////////////////////
711// ProcessSingleton
712//
[email protected]dd85d452013-03-28 12:39:59713ProcessSingleton::ProcessSingleton(
714 const base::FilePath& user_data_dir,
715 const NotificationCallback& notification_callback)
[email protected]9a47c432013-04-19 20:33:55716 : notification_callback_(notification_callback),
[email protected]65718d92012-05-02 23:02:58717 current_pid_(base::GetCurrentProcId()),
[email protected]9c009092013-05-01 03:14:09718 watcher_(new LinuxWatcher(this)) {
[email protected]1912cfe2009-04-21 08:09:30719 socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
[email protected]9f20a6d02009-08-21 01:18:37720 lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
[email protected]53f4826c2010-08-27 01:29:28721 cookie_path_ = user_data_dir.Append(chrome::kSingletonCookieFilename);
[email protected]65718d92012-05-02 23:02:58722
723 kill_callback_ = base::Bind(&ProcessSingleton::KillProcess,
724 base::Unretained(this));
[email protected]19d7e9682009-02-18 22:04:28725}
726
727ProcessSingleton::~ProcessSingleton() {
gab25894fe2017-05-30 03:40:36728 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
[email protected]19d7e9682009-02-18 22:04:28729}
730
[email protected]9f20a6d02009-08-21 01:18:37731ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
mattma92250e2014-09-09 07:26:16732 return NotifyOtherProcessWithTimeout(
avi556c05022014-12-22 23:31:43733 *base::CommandLine::ForCurrentProcess(), kRetryAttempts,
734 base::TimeDelta::FromSeconds(kTimeoutInSeconds), true);
[email protected]c0d297952009-09-17 21:00:18735}
736
737ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
avi556c05022014-12-22 23:31:43738 const base::CommandLine& cmd_line,
mattma92250e2014-09-09 07:26:16739 int retry_attempts,
740 const base::TimeDelta& timeout,
[email protected]4a44bc32010-05-28 22:22:44741 bool kill_unresponsive) {
mattma92250e2014-09-09 07:26:16742 DCHECK_GE(retry_attempts, 0);
743 DCHECK_GE(timeout.InMicroseconds(), 0);
744
745 base::TimeDelta sleep_interval = timeout / retry_attempts;
[email protected]4dd42242010-04-07 02:21:15746
[email protected]53f4826c2010-08-27 01:29:28747 ScopedSocket socket;
Leonard Greyfe15df92017-12-08 17:09:53748 int pid = 0;
mattma92250e2014-09-09 07:26:16749 for (int retries = 0; retries <= retry_attempts; ++retries) {
[email protected]53f4826c2010-08-27 01:29:28750 // Try to connect to the socket.
Leonard Greyfe15df92017-12-08 17:09:53751 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 Cameron670c4d92019-07-29 18:39:38756 ParseProcessSingletonLock(lock_path_, &hostname, &pid);
Leonard Greyfe15df92017-12-08 17:09:53757#endif
[email protected]4dd42242010-04-07 02:21:15758 break;
Leonard Greyfe15df92017-12-08 17:09:53759 }
[email protected]4dd42242010-04-07 02:21:15760
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
mattma92250e2014-09-09 07:26:16764 // chrome browser. If so, we loop and try again for |timeout|.
[email protected]4dd42242010-04-07 02:21:15765
766 std::string hostname;
Christopher Cameron670c4d92019-07-29 18:39:38767 if (!ParseProcessSingletonLock(lock_path_, &hostname, &pid)) {
[email protected]4dd42242010-04-07 02:21:15768 // No lockfile exists.
769 return PROCESS_NONE;
770 }
771
772 if (hostname.empty()) {
773 // Invalid lockfile.
[email protected]723571a2010-12-03 17:37:54774 UnlinkPath(lock_path_);
aseren028ea152017-05-16 17:22:43775 SendRemoteProcessInteractionResultHistogram(INVALID_LOCK_FILE);
[email protected]4dd42242010-04-07 02:21:15776 return PROCESS_NONE;
777 }
778
[email protected]37248d72013-09-11 04:31:27779 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_);
aseren028ea152017-05-16 17:22:43784 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED);
[email protected]37248d72013-09-11 04:31:27785 return PROCESS_NONE;
786 }
[email protected]9f20a6d02009-08-21 01:18:37787 return PROFILE_IN_USE;
[email protected]4dd42242010-04-07 02:21:15788 }
789
[email protected]a70d9cf2010-05-11 23:05:19790 if (!IsChromeProcess(pid)) {
[email protected]4dd42242010-04-07 02:21:15791 // Orphaned lockfile (no process with pid, or non-chrome process.)
[email protected]723571a2010-12-03 17:37:54792 UnlinkPath(lock_path_);
aseren028ea152017-05-16 17:22:43793 SendRemoteProcessInteractionResultHistogram(ORPHANED_LOCK_FILE);
[email protected]4dd42242010-04-07 02:21:15794 return PROCESS_NONE;
795 }
796
[email protected]a70d9cf2010-05-11 23:05:19797 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]723571a2010-12-03 17:37:54800 UnlinkPath(lock_path_);
aseren028ea152017-05-16 17:22:43801 SendRemoteProcessInteractionResultHistogram(SAME_BROWSER_INSTANCE);
[email protected]a70d9cf2010-05-11 23:05:19802 return PROCESS_NONE;
803 }
804
mattma92250e2014-09-09 07:26:16805 if (retries == retry_attempts) {
[email protected]4dd42242010-04-07 02:21:15806 // Retries failed. Kill the unresponsive chrome process and continue.
aseren670954482017-06-06 18:14:12807 if (!kill_unresponsive || !KillProcessByLockPath(false))
[email protected]4dd42242010-04-07 02:21:15808 return PROFILE_IN_USE;
aseren028ea152017-05-16 17:22:43809 SendRemoteHungProcessTerminateReasonHistogram(NOTIFY_ATTEMPTS_EXCEEDED);
[email protected]4dd42242010-04-07 02:21:15810 return PROCESS_NONE;
811 }
812
mattma92250e2014-09-09 07:26:16813 base::PlatformThread::Sleep(sleep_interval);
[email protected]9f20a6d02009-08-21 01:18:37814 }
[email protected]b674dc732009-05-20 20:41:00815
Leonard Greyfe15df92017-12-08 17:09:53816#if defined(OS_MACOSX)
817 if (pid > 0 && WaitForAndForwardOpenURLEvent(pid)) {
818 return PROCESS_NOTIFIED;
819 }
820#endif
mattma92250e2014-09-09 07:26:16821 timeval socket_timeout = TimeDeltaToTimeVal(timeout);
822 setsockopt(socket.fd(),
823 SOL_SOCKET,
824 SO_SNDTIMEO,
825 &socket_timeout,
826 sizeof(socket_timeout));
[email protected]b674dc732009-05-20 20:41:00827
828 // Found another process, prepare our command line
[email protected]52db4aa2009-05-21 18:41:02829 // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
[email protected]b674dc732009-05-20 20:41:00830 std::string to_send(kStartToken);
831 to_send.push_back(kTokenDelimiter);
832
[email protected]650b2d52013-02-10 03:41:45833 base::FilePath current_dir;
Avi Drissman9098f9002018-05-04 00:11:52834 if (!base::PathService::Get(base::DIR_CURRENT, &current_dir))
[email protected]9f20a6d02009-08-21 01:18:37835 return PROCESS_NONE;
[email protected]b674dc732009-05-20 20:41:00836 to_send.append(current_dir.value());
[email protected]b674dc732009-05-20 20:41:00837
[email protected]0189bbd2009-10-12 22:50:39838 const std::vector<std::string>& argv = cmd_line.argv();
jdoerrie2f1af512018-10-03 00:59:37839 for (auto it = argv.begin(); it != argv.end(); ++it) {
[email protected]b674dc732009-05-20 20:41:00840 to_send.push_back(kTokenDelimiter);
[email protected]52db4aa2009-05-21 18:41:02841 to_send.append(*it);
[email protected]19d7e9682009-02-18 22:04:28842 }
843
[email protected]b674dc732009-05-20 20:41:00844 // Send the message
[email protected]53f4826c2010-08-27 01:29:28845 if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
[email protected]8b08cbd2009-08-04 05:34:19846 // Try to kill the other process, because it might have been dead.
aseren670954482017-06-06 18:14:12847 if (!kill_unresponsive || !KillProcessByLockPath(true))
[email protected]9f20a6d02009-08-21 01:18:37848 return PROFILE_IN_USE;
aseren028ea152017-05-16 17:22:43849 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_WRITE_FAILED);
[email protected]9f20a6d02009-08-21 01:18:37850 return PROCESS_NONE;
[email protected]dc609a172009-07-31 05:31:27851 }
852
[email protected]53f4826c2010-08-27 01:29:28853 if (shutdown(socket.fd(), SHUT_WR) < 0)
[email protected]57b765672009-10-13 18:27:40854 PLOG(ERROR) << "shutdown() failed";
[email protected]dc609a172009-07-31 05:31:27855
[email protected]8b08cbd2009-08-04 05:34:19856 // 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];
mattma92250e2014-09-09 07:26:16859 ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout);
[email protected]8b08cbd2009-08-04 05:34:19860
861 // Failed to read ACK, the other process might have been frozen.
862 if (len <= 0) {
aseren670954482017-06-06 18:14:12863 if (!kill_unresponsive || !KillProcessByLockPath(true))
[email protected]9f20a6d02009-08-21 01:18:37864 return PROFILE_IN_USE;
aseren028ea152017-05-16 17:22:43865 SendRemoteHungProcessTerminateReasonHistogram(SOCKET_READ_FAILED);
[email protected]9f20a6d02009-08-21 01:18:37866 return PROCESS_NONE;
[email protected]8b08cbd2009-08-04 05:34:19867 }
868
869 buf[len] = '\0';
Avi Drissman5f0fb8c2018-12-25 23:20:49870 if (strncmp(buf, kShutdownToken, base::size(kShutdownToken) - 1) == 0) {
[email protected]8b08cbd2009-08-04 05:34:19871 // The other process is shutting down, it's safe to start a new process.
aseren028ea152017-05-16 17:22:43872 SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_SHUTTING_DOWN);
[email protected]9f20a6d02009-08-21 01:18:37873 return PROCESS_NONE;
Avi Drissman5f0fb8c2018-12-25 23:20:49874 } else if (strncmp(buf, kACKToken, base::size(kACKToken) - 1) == 0) {
[email protected]1abeec32014-07-16 05:56:11875#if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
[email protected]cc04a152014-02-12 09:17:16876 // 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]8b08cbd2009-08-04 05:34:19882 // Assume the other process is handling the request.
[email protected]9f20a6d02009-08-21 01:18:37883 return PROCESS_NOTIFIED;
[email protected]8b08cbd2009-08-04 05:34:19884 }
885
886 NOTREACHED() << "The other process returned unknown message: " << buf;
[email protected]9f20a6d02009-08-21 01:18:37887 return PROCESS_NOTIFIED;
[email protected]19d7e9682009-02-18 22:04:28888}
889
[email protected]dd85d452013-03-28 12:39:59890ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
[email protected]4a44bc32010-05-28 22:22:44891 return NotifyOtherProcessWithTimeoutOrCreate(
avi556c05022014-12-22 23:31:43892 *base::CommandLine::ForCurrentProcess(), kRetryAttempts,
mattma92250e2014-09-09 07:26:16893 base::TimeDelta::FromSeconds(kTimeoutInSeconds));
[email protected]4a44bc32010-05-28 22:22:44894}
895
896ProcessSingleton::NotifyResult
897ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate(
avi556c05022014-12-22 23:31:43898 const base::CommandLine& command_line,
mattma92250e2014-09-09 07:26:16899 int retry_attempts,
900 const base::TimeDelta& timeout) {
gab439f54f2016-10-07 22:24:22901 const base::TimeTicks begin_ticks = base::TimeTicks::Now();
mattma92250e2014-09-09 07:26:16902 NotifyResult result = NotifyOtherProcessWithTimeout(
903 command_line, retry_attempts, timeout, true);
gab439f54f2016-10-07 22:24:22904 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]4a44bc32010-05-28 22:22:44912 return result;
gab439f54f2016-10-07 22:24:22913 }
914
915 if (Create()) {
916 UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToCreate",
917 base::TimeTicks::Now() - begin_ticks);
[email protected]4a44bc32010-05-28 22:22:44918 return PROCESS_NONE;
gab439f54f2016-10-07 22:24:22919 }
920
[email protected]4a44bc32010-05-28 22:22:44921 // 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.
mattma92250e2014-09-09 07:26:16926 result = NotifyOtherProcessWithTimeout(
927 command_line, retry_attempts, timeout, false);
gab439f54f2016-10-07 22:24:22928
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]4a44bc32010-05-28 22:22:44937 if (result != PROCESS_NONE)
938 return result;
939
940 return LOCK_ERROR;
941}
942
[email protected]65718d92012-05-02 23:02:58943void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) {
944 current_pid_ = pid;
945}
946
947void ProcessSingleton::OverrideKillCallbackForTesting(
948 const base::Callback<void(int)>& callback) {
949 kill_callback_ = callback;
950}
951
aseren670954482017-06-06 18:14:12952// static
[email protected]65718d92012-05-02 23:02:58953void ProcessSingleton::DisablePromptForTesting() {
954 g_disable_prompt = true;
955}
956
aseren670954482017-06-06 18:14:12957// static
aseren028ea152017-05-16 17:22:43958void ProcessSingleton::SkipIsChromeProcessCheckForTesting(bool skip) {
959 g_skip_is_chrome_process_check = skip;
960}
961
aseren670954482017-06-06 18:14:12962// static
963void ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(
964 bool set_unlock) {
965 g_user_opted_unlock_in_use_profile = set_unlock;
966}
967
[email protected]dd85d452013-03-28 12:39:59968bool ProcessSingleton::Create() {
[email protected]19d7e9682009-02-18 22:04:28969 int sock;
970 sockaddr_un addr;
[email protected]19d7e9682009-02-18 22:04:28971
[email protected]9f20a6d02009-08-21 01:18:37972 // The symlink lock is pointed to the hostname and process id, so other
973 // processes can find it out.
Christopher Cameron670c4d92019-07-29 18:39:38974 base::FilePath symlink_content(
975 base::StringPrintf("%s%c%u", net::GetHostName().c_str(),
976 kProcessSingletonLockDelimiter, current_pid_));
[email protected]8b08cbd2009-08-04 05:34:19977
[email protected]9f20a6d02009-08-21 01:18:37978 // Create symbol link before binding the socket, to ensure only one instance
979 // can have the socket open.
[email protected]723571a2010-12-03 17:37:54980 if (!SymlinkPath(symlink_content, lock_path_)) {
[email protected]c17ecbe2014-05-01 10:50:05981 // 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]4dd42242010-04-07 02:21:15989 return false;
[email protected]c17ecbe2014-05-01 10:50:05990 }
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]9f20a6d02009-08-21 01:18:37996 }
[email protected]8b08cbd2009-08-04 05:34:19997
[email protected]53f4826c2010-08-27 01:29:28998 // 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]c17ecbe2014-05-01 10:50:051005
1006 // Check that the directory was created with the correct permissions.
1007 int dir_mode = 0;
vabr8023d872016-09-15 08:12:221008 CHECK(base::GetPosixFilePermissions(socket_dir_.GetPath(), &dir_mode) &&
[email protected]c17ecbe2014-05-01 10:50:051009 dir_mode == base::FILE_PERMISSION_USER_MASK)
1010 << "Temp directory mode is not 700: " << std::oct << dir_mode;
1011
mattm916edf162017-04-13 21:26:511012 // 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]650b2d52013-02-10 03:41:451015 base::FilePath socket_target_path =
vabr8023d872016-09-15 08:12:221016 socket_dir_.GetPath().Append(chrome::kSingletonSocketFilename);
mattm916edf162017-04-13 21:26:511017 SetupSocket(socket_target_path.value(), &sock, &addr);
1018
1019 // Setup the socket symlink and the two cookies.
[email protected]650b2d52013-02-10 03:41:451020 base::FilePath cookie(GenerateCookie());
1021 base::FilePath remote_cookie_path =
vabr8023d872016-09-15 08:12:221022 socket_dir_.GetPath().Append(chrome::kSingletonCookieFilename);
[email protected]723571a2010-12-03 17:37:541023 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]53f4826c2010-08-27 01:29:281028 // 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]2d57f5d2011-01-13 14:20:121031 if (!socket_dir_.Delete())
1032 LOG(ERROR) << "Encountered a problem when deleting socket directory.";
[email protected]53f4826c2010-08-27 01:29:281033 return false;
1034 }
1035
[email protected]ac39c522009-06-24 21:36:171036 if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
[email protected]53f4826c2010-08-27 01:29:281037 PLOG(ERROR) << "Failed to bind() " << socket_target_path.value();
[email protected]8b08cbd2009-08-04 05:34:191038 CloseSocket(sock);
[email protected]4dd42242010-04-07 02:21:151039 return false;
[email protected]ac39c522009-06-24 21:36:171040 }
[email protected]19d7e9682009-02-18 22:04:281041
1042 if (listen(sock, 5) < 0)
brettw6ee6fd62015-06-09 18:05:241043 NOTREACHED() << "listen failed: " << base::safe_strerror(errno);
[email protected]19d7e9682009-02-18 22:04:281044
Gabriel Charette2983181c2018-03-28 17:01:091045 DCHECK(BrowserThread::IsThreadInitialized(BrowserThread::IO));
Sami Kyostila7d640eb2019-07-31 18:50:261046 base::PostTask(FROM_HERE, {BrowserThread::IO},
1047 base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening,
1048 watcher_, sock));
[email protected]4dd42242010-04-07 02:21:151049
1050 return true;
[email protected]19d7e9682009-02-18 22:04:281051}
[email protected]9f20a6d02009-08-21 01:18:371052
1053void ProcessSingleton::Cleanup() {
[email protected]723571a2010-12-03 17:37:541054 UnlinkPath(socket_path_);
1055 UnlinkPath(cookie_path_);
1056 UnlinkPath(lock_path_);
[email protected]9f20a6d02009-08-21 01:18:371057}
[email protected]65718d92012-05-02 23:02:581058
1059bool ProcessSingleton::IsSameChromeInstance(pid_t pid) {
1060 pid_t cur_pid = current_pid_;
1061 while (pid != cur_pid) {
1062 pid = base::GetParentProcessId(pid);
Matt Mueller65ef3a72017-08-04 15:25:161063 if (pid <= 0)
[email protected]65718d92012-05-02 23:02:581064 return false;
1065 if (!IsChromeProcess(pid))
1066 return false;
1067 }
1068 return true;
1069}
1070
aseren670954482017-06-06 18:14:121071bool ProcessSingleton::KillProcessByLockPath(bool is_connected_to_socket) {
[email protected]65718d92012-05-02 23:02:581072 std::string hostname;
1073 int pid;
Christopher Cameron670c4d92019-07-29 18:39:381074 ParseProcessSingletonLock(lock_path_, &hostname, &pid);
[email protected]65718d92012-05-02 23:02:581075
aseren670954482017-06-06 18:14:121076 if (!hostname.empty() && hostname != net::GetHostName() &&
1077 !is_connected_to_socket) {
aseren028ea152017-05-16 17:22:431078 bool res = DisplayProfileInUseError(lock_path_, hostname, pid);
aseren670954482017-06-06 18:14:121079 if (res) {
1080 UnlinkPath(lock_path_);
aseren028ea152017-05-16 17:22:431081 SendRemoteProcessInteractionResultHistogram(PROFILE_UNLOCKED_BEFORE_KILL);
aseren670954482017-06-06 18:14:121082 }
aseren028ea152017-05-16 17:22:431083 return res;
[email protected]65718d92012-05-02 23:02:581084 }
1085 UnlinkPath(lock_path_);
1086
aseren028ea152017-05-16 17:22:431087 if (IsSameChromeInstance(pid)) {
1088 SendRemoteProcessInteractionResultHistogram(
1089 SAME_BROWSER_INSTANCE_BEFORE_KILL);
[email protected]65718d92012-05-02 23:02:581090 return true;
aseren028ea152017-05-16 17:22:431091 }
[email protected]65718d92012-05-02 23:02:581092
1093 if (pid > 0) {
1094 kill_callback_.Run(pid);
1095 return true;
1096 }
1097
aseren028ea152017-05-16 17:22:431098 SendRemoteProcessInteractionResultHistogram(FAILED_TO_EXTRACT_PID);
1099
[email protected]65718d92012-05-02 23:02:581100 LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value();
1101 return true;
1102}
1103
1104void 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: "
brettw6ee6fd62015-06-09 18:05:241110 << base::safe_strerror(errno);
aseren028ea152017-05-16 17:22:431111
1112 int error_code = (rv == 0) ? 0 : errno;
Ilya Sherman982457e62017-12-13 02:19:361113 base::UmaHistogramSparse(
aseren028ea152017-05-16 17:22:431114 "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]65718d92012-05-02 23:02:581131}