// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <sys/file.h>
#include "chrome/browser/process_singleton.h"
#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include "base/metrics/histogram.h"
#include "chrome/common/chrome_constants.h"
namespace {
// From "man 2 intro", the largest errno is |EOPNOTSUPP|, which is
// |102|. Since the histogram memory usage is proportional to this
// number, using the |102| directly rather than the macro.
const int kMaxErrno = 102;
} // namespace
// This class is used to funnel messages to a single instance of the browser
// process. This is needed for several reasons on other platforms.
//
// On Windows, when the user re-opens the application from the shell (e.g. an
// explicit double-click, a shortcut that opens a webpage, etc.) we need to send
// the message to the currently-existing copy of the browser.
//
// On Linux, opening a URL is done by creating an instance of the web browser
// process and passing it the URL to go to on its commandline.
//
// Neither of those cases apply on the Mac. Launch Services ensures that there
// is only one instance of the process, and we get URLs to open via AppleEvents
// and, once again, the Launch Services system. We have no need to manage this
// ourselves. An exclusive lock is used to flush out anyone making incorrect
// assumptions.
ProcessSingleton::ProcessSingleton(const FilePath& user_data_dir)
: locked_(false),
foreground_window_(NULL),
lock_path_(user_data_dir.Append(chrome::kSingletonLockFilename)),
lock_fd_(-1) {
}
ProcessSingleton::~ProcessSingleton() {
// Make sure the lock is released. Process death will also release
// it, even if this is not called.
Cleanup();
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
// This space intentionally left blank.
return PROCESS_NONE;
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
// Windows tries NotifyOtherProcess() first.
return Create() ? PROCESS_NONE : PROFILE_IN_USE;
}
// Attempt to acquire an exclusive lock on an empty file in the
// profile directory. Returns |true| if it gets the lock. Returns
// |false| if the lock is held, or if there is an error.
// TODO(shess): Rather than logging failures, popup an alert. Punting
// that for now because it would require confidence that this code is
// never called in a situation where an alert wouldn't work.
// http://crbug.com/59061
bool ProcessSingleton::Create() {
DCHECK_EQ(-1, lock_fd_) << "lock_path_ is already open.";
lock_fd_ = HANDLE_EINTR(open(lock_path_.value().c_str(),
O_RDONLY | O_CREAT, 0644));
if (lock_fd_ == -1) {
const int capture_errno = errno;
DPCHECK(lock_fd_ != -1) << "Unexpected failure opening profile lockfile";
UMA_HISTOGRAM_ENUMERATION("ProcessSingleton.OpenError",
capture_errno, kMaxErrno);
return false;
}
// Acquire an exclusive lock in non-blocking fashion. If the lock
// is already held, this will return |EWOULDBLOCK|.
int rc = HANDLE_EINTR(flock(lock_fd_, LOCK_EX|LOCK_NB));
if (rc == -1) {
const int capture_errno = errno;
DPCHECK(errno == EWOULDBLOCK)
<< "Unexpected failure locking profile lockfile";
Cleanup();
// Other errors indicate something crazy is happening.
if (capture_errno != EWOULDBLOCK) {
UMA_HISTOGRAM_ENUMERATION("ProcessSingleton.LockError",
capture_errno, kMaxErrno);
return false;
}
// The file is open by another process and locked.
LOG(ERROR) << "Unable to obtain profile lock.";
return false;
}
return true;
}
void ProcessSingleton::Cleanup() {
// Closing the file also releases the lock.
if (lock_fd_ != -1) {
int rc = HANDLE_EINTR(close(lock_fd_));
DPCHECK(!rc) << "Closing lock_fd_:";
}
lock_fd_ = -1;
}