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