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 15 namespace { 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. 20 const 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 40 ProcessSingleton::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 47 ProcessSingleton::~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 53 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { 54 // This space intentionally left blank. 55 return PROCESS_NONE; 56 } 57 58 ProcessSingleton::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 72 bool 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 110 void 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