This source file includes following definitions.
- SetNonBlocking
- SetCloseOnExec
- CloseSocket
- WriteToSocket
- WaitSocketForRead
- ReadFromSocket
- SetupSockAddr
- SetupSocketOnly
- SetupSocket
- ReadLink
- UnlinkPath
- SymlinkPath
- ParseLockPath
- DisplayProfileInUseError
- IsChromeProcess
- fd
- Reset
- Close
- GenerateCookie
- CheckCookie
- ConnectSocket
- bytes_read_
- OnFileCanWriteWithoutBlocking
- CleanupAndDeleteSelf
- parent_
- OnFileCanWriteWithoutBlocking
- WillDestroyCurrentMessageLoop
- OnFileCanReadWithoutBlocking
- StartListening
- HandleMessage
- RemoveSocketReader
- OnFileCanReadWithoutBlocking
- FinishWithACK
- watcher_
- NotifyOtherProcess
- NotifyOtherProcessWithTimeout
- NotifyOtherProcessOrCreate
- NotifyOtherProcessWithTimeoutOrCreate
- OverrideCurrentPidForTesting
- OverrideKillCallbackForTesting
- DisablePromptForTesting
- Create
- Cleanup
- IsSameChromeInstance
- KillProcessByLockPath
- KillProcess
#include "chrome/browser/process_singleton.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>
#include <set>
#include <string>
#include "base/base_paths.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h"
#include "base/safe_strerror_posix.h"
#include "base/sequenced_task_runner_helpers.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/ui/process_singleton_dialog_linux.h"
#include "chrome/common/chrome_constants.h"
#include "content/public/browser/browser_thread.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "net/base/net_util.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(TOOLKIT_GTK)
#include <gdk/gdk.h>
#endif
#if defined(TOOLKIT_VIEWS) && !defined(OS_CHROMEOS)
#include "ui/views/linux_ui/linux_ui.h"
#endif
using content::BrowserThread;
const int ProcessSingleton::kTimeoutInSeconds;
namespace {
static bool g_disable_prompt;
const char kStartToken[] = "START";
const char kACKToken[] = "ACK";
const char kShutdownToken[] = "SHUTDOWN";
const char kTokenDelimiter = '\0';
const int kMaxMessageLength = 32 * 1024;
const int kMaxACKMessageLength = arraysize(kShutdownToken) - 1;
const char kLockDelimiter = '-';
int SetNonBlocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (-1 == flags)
return flags;
if (flags & O_NONBLOCK)
return 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int SetCloseOnExec(int fd) {
int flags = fcntl(fd, F_GETFD, 0);
if (-1 == flags)
return flags;
if (flags & FD_CLOEXEC)
return 0;
return fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
void CloseSocket(int fd) {
int rv = IGNORE_EINTR(close(fd));
DCHECK_EQ(0, rv) << "Error closing socket: " << safe_strerror(errno);
}
bool WriteToSocket(int fd, const char *message, size_t length) {
DCHECK(message);
DCHECK(length);
size_t bytes_written = 0;
do {
ssize_t rv = HANDLE_EINTR(
write(fd, message + bytes_written, length - bytes_written));
if (rv < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
LOG(ERROR) << "ProcessSingleton would block on write(), so it gave up.";
return false;
}
PLOG(ERROR) << "write() failed";
return false;
}
bytes_written += rv;
} while (bytes_written < length);
return true;
}
int WaitSocketForRead(int fd, int timeout) {
fd_set read_fds;
struct timeval tv;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
tv.tv_sec = timeout;
tv.tv_usec = 0;
return HANDLE_EINTR(select(fd + 1, &read_fds, NULL, NULL, &tv));
}
ssize_t ReadFromSocket(int fd, char *buf, size_t bufsize, int timeout) {
if (timeout > 0) {
int rv = WaitSocketForRead(fd, timeout);
if (rv <= 0)
return rv;
}
size_t bytes_read = 0;
do {
ssize_t rv = HANDLE_EINTR(read(fd, buf + bytes_read, bufsize - bytes_read));
if (rv < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
PLOG(ERROR) << "read() failed";
return rv;
} else {
return bytes_read;
}
} else if (!rv) {
return bytes_read;
} else {
bytes_read += rv;
}
} while (bytes_read < bufsize);
return bytes_read;
}
void SetupSockAddr(const std::string& path, struct sockaddr_un* addr) {
addr->sun_family = AF_UNIX;
CHECK(path.length() < arraysize(addr->sun_path))
<< "Socket path too long: " << path;
base::strlcpy(addr->sun_path, path.c_str(), arraysize(addr->sun_path));
}
int SetupSocketOnly() {
int sock = socket(PF_UNIX, SOCK_STREAM, 0);
PCHECK(sock >= 0) << "socket() failed";
int rv = SetNonBlocking(sock);
DCHECK_EQ(0, rv) << "Failed to make non-blocking socket.";
rv = SetCloseOnExec(sock);
DCHECK_EQ(0, rv) << "Failed to set CLOEXEC on socket.";
return sock;
}
void SetupSocket(const std::string& path, int* sock, struct sockaddr_un* addr) {
*sock = SetupSocketOnly();
SetupSockAddr(path, addr);
}
base::FilePath ReadLink(const base::FilePath& path) {
base::FilePath target;
if (!base::ReadSymbolicLink(path, &target)) {
if (errno != 0 && errno != ENOENT)
PLOG(ERROR) << "readlink(" << path.value() << ") failed";
}
return target;
}
bool UnlinkPath(const base::FilePath& path) {
int rv = unlink(path.value().c_str());
if (rv < 0 && errno != ENOENT)
PLOG(ERROR) << "Failed to unlink " << path.value();
return rv == 0;
}
bool SymlinkPath(const base::FilePath& target, const base::FilePath& path) {
if (!base::CreateSymbolicLink(target, path)) {
int saved_errno = errno;
if (ReadLink(path) != target) {
errno = saved_errno;
PLOG(ERROR) << "Failed to create " << path.value();
return false;
}
}
return true;
}
bool ParseLockPath(const base::FilePath& path,
std::string* hostname,
int* pid) {
std::string real_path = ReadLink(path).value();
if (real_path.empty())
return false;
std::string::size_type pos = real_path.rfind(kLockDelimiter);
if (pos == std::string::npos) {
*hostname = "";
*pid = -1;
return true;
}
*hostname = real_path.substr(0, pos);
const std::string& pid_str = real_path.substr(pos + 1);
if (!base::StringToInt(pid_str, pid))
*pid = -1;
return true;
}
bool DisplayProfileInUseError(const base::FilePath& lock_path,
const std::string& hostname,
int pid) {
base::string16 error = l10n_util::GetStringFUTF16(
IDS_PROFILE_IN_USE_LINUX,
base::IntToString16(pid),
base::ASCIIToUTF16(hostname));
base::string16 relaunch_button_text = l10n_util::GetStringUTF16(
IDS_PROFILE_IN_USE_LINUX_RELAUNCH);
LOG(ERROR) << base::SysWideToNativeMB(base::UTF16ToWide(error)).c_str();
if (!g_disable_prompt)
return ShowProcessSingletonDialog(error, relaunch_button_text);
return false;
}
bool IsChromeProcess(pid_t pid) {
base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid));
return (!other_chrome_path.empty() &&
other_chrome_path.BaseName() ==
base::FilePath(chrome::kBrowserProcessExecutableName));
}
class ScopedSocket {
public:
ScopedSocket() : fd_(-1) { Reset(); }
~ScopedSocket() { Close(); }
int fd() { return fd_; }
void Reset() {
Close();
fd_ = SetupSocketOnly();
}
void Close() {
if (fd_ >= 0)
CloseSocket(fd_);
fd_ = -1;
}
private:
int fd_;
};
std::string GenerateCookie() {
return base::Uint64ToString(base::RandUint64());
}
bool CheckCookie(const base::FilePath& path, const base::FilePath& cookie) {
return (cookie == ReadLink(path));
}
bool ConnectSocket(ScopedSocket* socket,
const base::FilePath& socket_path,
const base::FilePath& cookie_path) {
base::FilePath socket_target;
if (base::ReadSymbolicLink(socket_path, &socket_target)) {
base::FilePath cookie = ReadLink(cookie_path);
if (cookie.empty())
return false;
base::FilePath remote_cookie = socket_target.DirName().
Append(chrome::kSingletonCookieFilename);
if (!CheckCookie(remote_cookie, cookie))
return false;
sockaddr_un addr;
SetupSockAddr(socket_path.value(), &addr);
int ret = HANDLE_EINTR(connect(socket->fd(),
reinterpret_cast<sockaddr*>(&addr),
sizeof(addr)));
if (ret != 0)
return false;
if (!CheckCookie(remote_cookie, cookie)) {
socket->Reset();
return false;
}
return true;
} else if (errno == EINVAL) {
sockaddr_un addr;
SetupSockAddr(socket_path.value(), &addr);
int ret = HANDLE_EINTR(connect(socket->fd(),
reinterpret_cast<sockaddr*>(&addr),
sizeof(addr)));
return (ret == 0);
} else {
if (errno != ENOENT)
PLOG(ERROR) << "readlink failed";
return false;
}
}
}
class ProcessSingleton::LinuxWatcher
: public base::MessageLoopForIO::Watcher,
public base::MessageLoop::DestructionObserver,
public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher,
BrowserThread::DeleteOnIOThread> {
public:
class SocketReader : public base::MessageLoopForIO::Watcher {
public:
SocketReader(ProcessSingleton::LinuxWatcher* parent,
base::MessageLoop* ui_message_loop,
int fd)
: parent_(parent),
ui_message_loop_(ui_message_loop),
fd_(fd),
bytes_read_(0) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
base::MessageLoopForIO::current()->WatchFileDescriptor(
fd, true, base::MessageLoopForIO::WATCH_READ, &fd_reader_, this);
timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeoutInSeconds),
this, &SocketReader::CleanupAndDeleteSelf);
}
virtual ~SocketReader() {
CloseSocket(fd_);
}
virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
NOTREACHED();
}
void FinishWithACK(const char *message, size_t length);
private:
void CleanupAndDeleteSelf() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
parent_->RemoveSocketReader(this);
}
base::MessageLoopForIO::FileDescriptorWatcher fd_reader_;
ProcessSingleton::LinuxWatcher* const parent_;
base::MessageLoop* const ui_message_loop_;
const int fd_;
char buf_[kMaxMessageLength];
size_t bytes_read_;
base::OneShotTimer<SocketReader> timer_;
DISALLOW_COPY_AND_ASSIGN(SocketReader);
};
explicit LinuxWatcher(ProcessSingleton* parent)
: ui_message_loop_(base::MessageLoop::current()),
parent_(parent) {
}
void StartListening(int socket);
void HandleMessage(const std::string& current_dir,
const std::vector<std::string>& argv,
SocketReader* reader);
virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
NOTREACHED();
}
virtual void WillDestroyCurrentMessageLoop() OVERRIDE {
fd_watcher_.StopWatchingFileDescriptor();
}
private:
friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
friend class base::DeleteHelper<ProcessSingleton::LinuxWatcher>;
virtual ~LinuxWatcher() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
STLDeleteElements(&readers_);
base::MessageLoopForIO* ml = base::MessageLoopForIO::current();
ml->RemoveDestructionObserver(this);
}
void RemoveSocketReader(SocketReader* reader);
base::MessageLoopForIO::FileDescriptorWatcher fd_watcher_;
base::MessageLoop* ui_message_loop_;
ProcessSingleton* const parent_;
std::set<SocketReader*> readers_;
DISALLOW_COPY_AND_ASSIGN(LinuxWatcher);
};
void ProcessSingleton::LinuxWatcher::OnFileCanReadWithoutBlocking(int fd) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
sockaddr_un from;
socklen_t from_len = sizeof(from);
int connection_socket = HANDLE_EINTR(accept(
fd, reinterpret_cast<sockaddr*>(&from), &from_len));
if (-1 == connection_socket) {
PLOG(ERROR) << "accept() failed";
return;
}
int rv = SetNonBlocking(connection_socket);
DCHECK_EQ(0, rv) << "Failed to make non-blocking socket.";
SocketReader* reader = new SocketReader(this,
ui_message_loop_,
connection_socket);
readers_.insert(reader);
}
void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
base::MessageLoopForIO* ml = base::MessageLoopForIO::current();
ml->AddDestructionObserver(this);
ml->WatchFileDescriptor(socket, true, base::MessageLoopForIO::WATCH_READ,
&fd_watcher_, this);
}
void ProcessSingleton::LinuxWatcher::HandleMessage(
const std::string& current_dir, const std::vector<std::string>& argv,
SocketReader* reader) {
DCHECK(ui_message_loop_ == base::MessageLoop::current());
DCHECK(reader);
if (parent_->notification_callback_.Run(CommandLine(argv),
base::FilePath(current_dir))) {
reader->FinishWithACK(kACKToken, arraysize(kACKToken) - 1);
} else {
LOG(WARNING) << "Not handling interprocess notification as browser"
" is shutting down";
reader->FinishWithACK(kShutdownToken, arraysize(kShutdownToken) - 1);
return;
}
}
void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(reader);
readers_.erase(reader);
delete reader;
}
void ProcessSingleton::LinuxWatcher::SocketReader::OnFileCanReadWithoutBlocking(
int fd) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(fd, fd_);
while (bytes_read_ < sizeof(buf_)) {
ssize_t rv = HANDLE_EINTR(
read(fd, buf_ + bytes_read_, sizeof(buf_) - bytes_read_));
if (rv < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
PLOG(ERROR) << "read() failed";
CloseSocket(fd);
return;
} else {
return;
}
} else if (!rv) {
break;
} else {
bytes_read_ += rv;
}
}
const size_t kMinMessageLength = arraysize(kStartToken) + 4;
if (bytes_read_ < kMinMessageLength) {
buf_[bytes_read_] = 0;
LOG(ERROR) << "Invalid socket message (wrong length):" << buf_;
CleanupAndDeleteSelf();
return;
}
std::string str(buf_, bytes_read_);
std::vector<std::string> tokens;
base::SplitString(str, kTokenDelimiter, &tokens);
if (tokens.size() < 3 || tokens[0] != kStartToken) {
LOG(ERROR) << "Wrong message format: " << str;
CleanupAndDeleteSelf();
return;
}
timer_.Stop();
std::string current_dir = tokens[1];
tokens.erase(tokens.begin());
tokens.erase(tokens.begin());
ui_message_loop_->PostTask(FROM_HERE, base::Bind(
&ProcessSingleton::LinuxWatcher::HandleMessage,
parent_,
current_dir,
tokens,
this));
fd_reader_.StopWatchingFileDescriptor();
}
void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
const char *message, size_t length) {
if (message && length) {
WriteToSocket(fd_, message, length);
}
if (shutdown(fd_, SHUT_WR) < 0)
PLOG(ERROR) << "shutdown() failed";
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&ProcessSingleton::LinuxWatcher::RemoveSocketReader,
parent_,
this));
}
ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
const NotificationCallback& notification_callback)
: notification_callback_(notification_callback),
current_pid_(base::GetCurrentProcId()),
watcher_(new LinuxWatcher(this)) {
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
cookie_path_ = user_data_dir.Append(chrome::kSingletonCookieFilename);
kill_callback_ = base::Bind(&ProcessSingleton::KillProcess,
base::Unretained(this));
}
ProcessSingleton::~ProcessSingleton() {
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
return NotifyOtherProcessWithTimeout(*CommandLine::ForCurrentProcess(),
kTimeoutInSeconds,
true);
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
const CommandLine& cmd_line,
int timeout_seconds,
bool kill_unresponsive) {
DCHECK_GE(timeout_seconds, 0);
ScopedSocket socket;
for (int retries = 0; retries <= timeout_seconds; ++retries) {
if (ConnectSocket(&socket, socket_path_, cookie_path_))
break;
std::string hostname;
int pid;
if (!ParseLockPath(lock_path_, &hostname, &pid)) {
return PROCESS_NONE;
}
if (hostname.empty()) {
UnlinkPath(lock_path_);
return PROCESS_NONE;
}
if (hostname != net::GetHostName() && !IsChromeProcess(pid)) {
if (DisplayProfileInUseError(lock_path_, hostname, pid)) {
UnlinkPath(lock_path_);
return PROCESS_NONE;
}
return PROFILE_IN_USE;
}
if (!IsChromeProcess(pid)) {
UnlinkPath(lock_path_);
return PROCESS_NONE;
}
if (IsSameChromeInstance(pid)) {
UnlinkPath(lock_path_);
return PROCESS_NONE;
}
if (retries == timeout_seconds) {
if (!kill_unresponsive || !KillProcessByLockPath())
return PROFILE_IN_USE;
return PROCESS_NONE;
}
base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
}
timeval timeout = {timeout_seconds, 0};
setsockopt(socket.fd(), SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
std::string to_send(kStartToken);
to_send.push_back(kTokenDelimiter);
base::FilePath current_dir;
if (!PathService::Get(base::DIR_CURRENT, ¤t_dir))
return PROCESS_NONE;
to_send.append(current_dir.value());
const std::vector<std::string>& argv = cmd_line.argv();
for (std::vector<std::string>::const_iterator it = argv.begin();
it != argv.end(); ++it) {
to_send.push_back(kTokenDelimiter);
to_send.append(*it);
}
if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
if (!kill_unresponsive || !KillProcessByLockPath())
return PROFILE_IN_USE;
return PROCESS_NONE;
}
if (shutdown(socket.fd(), SHUT_WR) < 0)
PLOG(ERROR) << "shutdown() failed";
char buf[kMaxACKMessageLength + 1];
ssize_t len =
ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout_seconds);
if (len <= 0) {
if (!kill_unresponsive || !KillProcessByLockPath())
return PROFILE_IN_USE;
return PROCESS_NONE;
}
buf[len] = '\0';
if (strncmp(buf, kShutdownToken, arraysize(kShutdownToken) - 1) == 0) {
return PROCESS_NONE;
} else if (strncmp(buf, kACKToken, arraysize(kACKToken) - 1) == 0) {
#if defined(TOOLKIT_GTK)
gdk_notify_startup_complete();
#endif
#if defined(TOOLKIT_VIEWS) && !defined(OS_CHROMEOS)
views::LinuxUI* linux_ui = views::LinuxUI::instance();
if (linux_ui)
linux_ui->NotifyWindowManagerStartupComplete();
#endif
return PROCESS_NOTIFIED;
}
NOTREACHED() << "The other process returned unknown message: " << buf;
return PROCESS_NOTIFIED;
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
return NotifyOtherProcessWithTimeoutOrCreate(
*CommandLine::ForCurrentProcess(),
kTimeoutInSeconds);
}
ProcessSingleton::NotifyResult
ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate(
const CommandLine& command_line,
int timeout_seconds) {
NotifyResult result = NotifyOtherProcessWithTimeout(command_line,
timeout_seconds, true);
if (result != PROCESS_NONE)
return result;
if (Create())
return PROCESS_NONE;
result = NotifyOtherProcessWithTimeout(command_line, timeout_seconds, false);
if (result != PROCESS_NONE)
return result;
return LOCK_ERROR;
}
void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) {
current_pid_ = pid;
}
void ProcessSingleton::OverrideKillCallbackForTesting(
const base::Callback<void(int)>& callback) {
kill_callback_ = callback;
}
void ProcessSingleton::DisablePromptForTesting() {
g_disable_prompt = true;
}
bool ProcessSingleton::Create() {
int sock;
sockaddr_un addr;
base::FilePath symlink_content(base::StringPrintf(
"%s%c%u",
net::GetHostName().c_str(),
kLockDelimiter,
current_pid_));
if (!SymlinkPath(symlink_content, lock_path_)) {
return false;
}
if (!socket_dir_.CreateUniqueTempDir()) {
LOG(ERROR) << "Failed to create socket directory.";
return false;
}
base::FilePath socket_target_path =
socket_dir_.path().Append(chrome::kSingletonSocketFilename);
base::FilePath cookie(GenerateCookie());
base::FilePath remote_cookie_path =
socket_dir_.path().Append(chrome::kSingletonCookieFilename);
UnlinkPath(socket_path_);
UnlinkPath(cookie_path_);
if (!SymlinkPath(socket_target_path, socket_path_) ||
!SymlinkPath(cookie, cookie_path_) ||
!SymlinkPath(cookie, remote_cookie_path)) {
LOG(ERROR) << "Failed to create symlinks.";
if (!socket_dir_.Delete())
LOG(ERROR) << "Encountered a problem when deleting socket directory.";
return false;
}
SetupSocket(socket_target_path.value(), &sock, &addr);
if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
PLOG(ERROR) << "Failed to bind() " << socket_target_path.value();
CloseSocket(sock);
return false;
}
if (listen(sock, 5) < 0)
NOTREACHED() << "listen failed: " << safe_strerror(errno);
DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&ProcessSingleton::LinuxWatcher::StartListening,
watcher_.get(),
sock));
return true;
}
void ProcessSingleton::Cleanup() {
UnlinkPath(socket_path_);
UnlinkPath(cookie_path_);
UnlinkPath(lock_path_);
}
bool ProcessSingleton::IsSameChromeInstance(pid_t pid) {
pid_t cur_pid = current_pid_;
while (pid != cur_pid) {
pid = base::GetParentProcessId(pid);
if (pid < 0)
return false;
if (!IsChromeProcess(pid))
return false;
}
return true;
}
bool ProcessSingleton::KillProcessByLockPath() {
std::string hostname;
int pid;
ParseLockPath(lock_path_, &hostname, &pid);
if (!hostname.empty() && hostname != net::GetHostName()) {
return DisplayProfileInUseError(lock_path_, hostname, pid);
}
UnlinkPath(lock_path_);
if (IsSameChromeInstance(pid))
return true;
if (pid > 0) {
kill_callback_.Run(pid);
return true;
}
LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value();
return true;
}
void ProcessSingleton::KillProcess(int pid) {
int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL);
DCHECK(rv == 0 || errno == ESRCH) << "Error killing process: "
<< safe_strerror(errno);
}