1// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <errno.h>
6#include <fcntl.h>
7#include <sys/file.h>
8
9#include "chrome/browser/process_singleton.h"
10
11#include "base/eintr_wrapper.h"
12#include "base/file_util.h"
13#include "base/metrics/histogram.h"
14#include "chrome/common/chrome_constants.h"
15
16namespace {
17
18// From "man 2 intro", the largest errno is |EOPNOTSUPP|, which is
19// |102|.  Since the histogram memory usage is proportional to this
20// number, using the |102| directly rather than the macro.
21const int kMaxErrno = 102;
22
23}  // namespace
24
25// This class is used to funnel messages to a single instance of the browser
26// process. This is needed for several reasons on other platforms.
27//
28// On Windows, when the user re-opens the application from the shell (e.g. an
29// explicit double-click, a shortcut that opens a webpage, etc.) we need to send
30// the message to the currently-existing copy of the browser.
31//
32// On Linux, opening a URL is done by creating an instance of the web browser
33// process and passing it the URL to go to on its commandline.
34//
35// Neither of those cases apply on the Mac. Launch Services ensures that there
36// is only one instance of the process, and we get URLs to open via AppleEvents
37// and, once again, the Launch Services system. We have no need to manage this
38// ourselves.  An exclusive lock is used to flush out anyone making incorrect
39// assumptions.
40
41ProcessSingleton::ProcessSingleton(const FilePath& user_data_dir)
42    : locked_(false),
43      foreground_window_(NULL),
44      lock_path_(user_data_dir.Append(chrome::kSingletonLockFilename)),
45      lock_fd_(-1) {
46}
47
48ProcessSingleton::~ProcessSingleton() {
49  // Make sure the lock is released.  Process death will also release
50  // it, even if this is not called.
51  Cleanup();
52}
53
54ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
55  // This space intentionally left blank.
56  return PROCESS_NONE;
57}
58
59ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
60  // Windows tries NotifyOtherProcess() first.
61  return Create() ? PROCESS_NONE : PROFILE_IN_USE;
62}
63
64// Attempt to acquire an exclusive lock on an empty file in the
65// profile directory.  Returns |true| if it gets the lock.  Returns
66// |false| if the lock is held, or if there is an error.
67// TODO(shess): Rather than logging failures, popup an alert.  Punting
68// that for now because it would require confidence that this code is
69// never called in a situation where an alert wouldn't work.
70// http://crbug.com/59061
71bool ProcessSingleton::Create() {
72  DCHECK_EQ(-1, lock_fd_) << "lock_path_ is already open.";
73
74  lock_fd_ = HANDLE_EINTR(open(lock_path_.value().c_str(),
75                               O_RDONLY | O_CREAT, 0644));
76  if (lock_fd_ == -1) {
77    const int capture_errno = errno;
78    DPCHECK(lock_fd_ != -1) << "Unexpected failure opening profile lockfile";
79    UMA_HISTOGRAM_ENUMERATION("ProcessSingleton.OpenError",
80                              capture_errno, kMaxErrno);
81    return false;
82  }
83
84  // Acquire an exclusive lock in non-blocking fashion.  If the lock
85  // is already held, this will return |EWOULDBLOCK|.
86  int rc = HANDLE_EINTR(flock(lock_fd_, LOCK_EX|LOCK_NB));
87  if (rc == -1) {
88    const int capture_errno = errno;
89    DPCHECK(errno == EWOULDBLOCK)
90        << "Unexpected failure locking profile lockfile";
91
92    Cleanup();
93
94    // Other errors indicate something crazy is happening.
95    if (capture_errno != EWOULDBLOCK) {
96      UMA_HISTOGRAM_ENUMERATION("ProcessSingleton.LockError",
97                                capture_errno, kMaxErrno);
98      return false;
99    }
100
101    // The file is open by another process and locked.
102    LOG(ERROR) << "Unable to obtain profile lock.";
103    return false;
104  }
105
106  return true;
107}
108
109void ProcessSingleton::Cleanup() {
110  // Closing the file also releases the lock.
111  if (lock_fd_ != -1) {
112    int rc = HANDLE_EINTR(close(lock_fd_));
113    DPCHECK(!rc) << "Closing lock_fd_:";
114  }
115  lock_fd_ = -1;
116}
117