| OLD | NEW |
| (Empty) |
| 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 } | |
| OLD | NEW |