summaryrefslogtreecommitdiffstats
path: root/chrome/browser/process_singleton_linux.cc
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-07 02:21:15 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-07 02:21:15 +0000
commit4dd4224c9701c98a912bfd84dfcd24be2ce47d38 (patch)
treed4883c36bedc8ebd656052a169ea82492b2fa7d5 /chrome/browser/process_singleton_linux.cc
parent240faaf618d2247aa7da87fab9360cac95c3ba38 (diff)
downloadchromium_src-4dd4224c9701c98a912bfd84dfcd24be2ce47d38.zip
chromium_src-4dd4224c9701c98a912bfd84dfcd24be2ce47d38.tar.gz
chromium_src-4dd4224c9701c98a912bfd84dfcd24be2ce47d38.tar.bz2
Linux: fix startup race between creating the SingletonLock and listening on SingletonSocket.
BUG=39922 TEST=see bug Review URL: http://codereview.chromium.org/1612006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43801 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/process_singleton_linux.cc')
-rw-r--r--chrome/browser/process_singleton_linux.cc107
1 files changed, 68 insertions, 39 deletions
diff --git a/chrome/browser/process_singleton_linux.cc b/chrome/browser/process_singleton_linux.cc
index 10d6321..a012aea 100644
--- a/chrome/browser/process_singleton_linux.cc
+++ b/chrome/browser/process_singleton_linux.cc
@@ -48,9 +48,11 @@
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/path_service.h"
+#include "base/platform_thread.h"
#include "base/process_util.h"
#include "base/safe_strerror_posix.h"
#include "base/stl_util-inl.h"
@@ -235,10 +237,14 @@ bool UnlinkPath(const std::string& path) {
}
// Extract the hostname and pid from the lock symlink.
-std::string ParseLockPath(const std::string& path,
- std::string* hostname,
- int* pid) {
+// Returns true if the lock existed.
+bool ParseLockPath(const std::string& path,
+ std::string* hostname,
+ int* pid) {
std::string real_path = ReadLink(path);
+ if (real_path.empty())
+ return false;
+
std::string::size_type pos = real_path.rfind('-');
// If the path is not a symbolic link, or doesn't contain what we expect,
@@ -246,7 +252,7 @@ std::string ParseLockPath(const std::string& path,
if (pos == std::string::npos) {
*hostname = "";
*pid = -1;
- return "";
+ return true;
}
*hostname = real_path.substr(0, pos);
@@ -255,7 +261,7 @@ std::string ParseLockPath(const std::string& path,
if (!StringToInt(pid_str, pid))
*pid = -1;
- return real_path;
+ return true;
}
void DisplayProfileInUseError(const std::string& lock_path,
@@ -274,21 +280,6 @@ void DisplayProfileInUseError(const std::string& lock_path,
#endif
}
-// Check if the lock is on a different host. If so, return false. If not,
-// unlink the lock file and return true.
-bool CheckLockHostnameAndCleanup(const std::string& path) {
- std::string hostname;
- int pid;
- ParseLockPath(path, &hostname, &pid);
-
- if (!hostname.empty() && hostname != net::GetHostName()) {
- DisplayProfileInUseError(path, hostname, pid);
- return false;
- }
- UnlinkPath(path);
- return true;
-}
-
// Extract the process's pid from a symbol link path and if it is on
// the same host, kill the process, unlink the lock file and return true.
// If the process is on a different host, return false.
@@ -635,6 +626,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
const CommandLine& cmd_line,
int timeout_seconds) {
+ DCHECK_GE(timeout_seconds, 0);
+
int socket;
sockaddr_un addr;
SetupSocket(socket_path_.value(), &socket, &addr);
@@ -642,18 +635,55 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
// It'll close the socket automatically when exiting this method.
SocketCloser socket_closer(socket);
- // Connecting to the socket
- int ret = HANDLE_EINTR(connect(socket,
- reinterpret_cast<sockaddr*>(&addr),
- sizeof(addr)));
- if (ret < 0) {
- // TODO(mattm): there is a bit of a race here if another instance on the
- // same host is in Create() and has created the lock but not attached to the
- // socket. Our CheckLockHostnameAndCleanup call will clean up their lock
- // and allow us to create a new one.
- if (!CheckLockHostnameAndCleanup(lock_path_.value()))
+ for (int retries = 0; retries <= timeout_seconds; ++retries) {
+ // Connecting to the socket
+ int ret = HANDLE_EINTR(connect(socket,
+ reinterpret_cast<sockaddr*>(&addr),
+ sizeof(addr)));
+ if (ret == 0)
+ break;
+
+ // If we're in a race with another process, they may be in Create() and have
+ // created the lock but not attached to the socket. So we check if the
+ // process with the pid from the lockfile is currently running and is a
+ // chrome browser. If so, we loop and try again for |timeout_seconds|.
+
+ std::string hostname;
+ int pid;
+ if (!ParseLockPath(lock_path_.value(), &hostname, &pid)) {
+ // No lockfile exists.
+ return PROCESS_NONE;
+ }
+
+ if (hostname.empty()) {
+ // Invalid lockfile.
+ UnlinkPath(lock_path_.value());
+ return PROCESS_NONE;
+ }
+
+ if (hostname != net::GetHostName()) {
+ // Locked by process on another host.
+ DisplayProfileInUseError(lock_path_.value(), hostname, pid);
return PROFILE_IN_USE;
- return PROCESS_NONE; // Tell the caller there's nobody to notify.
+ }
+
+ FilePath other_chrome_path(base::GetProcessExecutablePath(pid));
+ if (other_chrome_path.empty() ||
+ other_chrome_path.BaseName() !=
+ FilePath::FromWStringHack(chrome::kBrowserProcessExecutableName)) {
+ // Orphaned lockfile (no process with pid, or non-chrome process.)
+ UnlinkPath(lock_path_.value());
+ return PROCESS_NONE;
+ }
+
+ if (retries == timeout_seconds) {
+ // Retries failed. Kill the unresponsive chrome process and continue.
+ if (!KillProcessByLockPath(lock_path_.value()))
+ return PROFILE_IN_USE;
+ return PROCESS_NONE;
+ }
+
+ PlatformThread::Sleep(1000 /* ms */);
}
timeval timeout = {timeout_seconds, 0};
@@ -713,7 +743,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
return PROCESS_NOTIFIED;
}
-void ProcessSingleton::Create() {
+bool ProcessSingleton::Create() {
int sock;
sockaddr_un addr;
@@ -737,7 +767,8 @@ void ProcessSingleton::Create() {
// TODO(mattm): If the other instance is on the same host, we could try
// to notify it rather than just failing.
errno = saved_errno;
- PLOG(FATAL) << "Failed to create " << lock_path_.value();
+ PLOG(ERROR) << "Failed to create " << lock_path_.value();
+ return false;
}
}
@@ -746,13 +777,9 @@ void ProcessSingleton::Create() {
UnlinkPath(socket_path_.value());
if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
- PLOG(ERROR) << "bind() failed";
- LOG(ERROR) << "SingletonSocket failed to create a socket in your home "
- "directory. This means that running multiple instances of "
- "the Chrome binary will start multiple browser process "
- "rather than opening a new window in the existing process.";
+ PLOG(ERROR) << "Failed to bind() " << socket_path_.value();
CloseSocket(sock);
- return;
+ return false;
}
if (listen(sock, 5) < 0)
@@ -767,6 +794,8 @@ void ProcessSingleton::Create() {
watcher_.get(),
&ProcessSingleton::LinuxWatcher::StartListening,
sock));
+
+ return true;
}
void ProcessSingleton::Cleanup() {