diff options
author | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-07 02:21:15 +0000 |
---|---|---|
committer | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-07 02:21:15 +0000 |
commit | 4dd4224c9701c98a912bfd84dfcd24be2ce47d38 (patch) | |
tree | d4883c36bedc8ebd656052a169ea82492b2fa7d5 /chrome/browser/process_singleton_linux.cc | |
parent | 240faaf618d2247aa7da87fab9360cac95c3ba38 (diff) | |
download | chromium_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.cc | 107 |
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() { |