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 |