OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/common/service_process_util.h" | 5 #include "chrome/common/service_process_util.h" |
6 | 6 |
7 #include <signal.h> | |
8 #include <unistd.h> | |
9 | |
7 #include "base/file_util.h" | 10 #include "base/file_util.h" |
8 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/message_loop.h" | |
13 #include "base/message_pump_libevent.h" | |
9 #include "base/path_service.h" | 14 #include "base/path_service.h" |
10 #include "chrome/common/chrome_paths.h" | 15 #include "chrome/common/chrome_paths.h" |
11 #include "chrome/common/chrome_version_info.h" | 16 #include "chrome/common/chrome_version_info.h" |
17 #include "chrome/common/multi_process_lock.h" | |
12 | 18 |
13 namespace { | 19 namespace { |
14 | 20 |
21 int g_signal_socket = -1; | |
22 | |
15 // Gets the name of the lock file for service process. | 23 // Gets the name of the lock file for service process. |
16 FilePath GetServiceProcessLockFilePath() { | 24 FilePath GetServiceProcessLockFilePath() { |
17 FilePath user_data_dir; | 25 FilePath user_data_dir; |
18 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); | 26 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); |
19 chrome::VersionInfo version_info; | 27 chrome::VersionInfo version_info; |
20 std::string lock_file_name = version_info.Version() + "Service Process Lock"; | 28 std::string lock_file_name = version_info.Version() + "Service Process Lock"; |
21 return user_data_dir.Append(lock_file_name); | 29 return user_data_dir.Append(lock_file_name); |
22 } | 30 } |
23 | 31 |
32 // Attempts to take a lock named |name|. If |waiting| then is will | |
Scott Byer
2011/02/02 00:12:38
nit: then this will
dmac
2011/02/02 00:56:25
Done.
| |
33 // make multiple attempts before failing. | |
34 // Caller is responsible for ownership of the MultiProcessLock. | |
35 MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting) { | |
36 scoped_ptr<MultiProcessLock> lock(MultiProcessLock::Create(name)); | |
37 if (lock == NULL) return NULL; | |
38 bool got_lock = false; | |
39 for (int i = 0; i < 10; ++i) { | |
40 if (lock->TryLock()) { | |
41 got_lock = true; | |
42 break; | |
43 } | |
44 if (!waiting) break; | |
45 base::PlatformThread::Sleep(100 * i); | |
46 } | |
47 if (!got_lock) { | |
48 lock.reset(); | |
49 } | |
50 return lock.release(); | |
51 } | |
52 | |
53 MultiProcessLock* TakeServiceRunningLock(bool waiting) { | |
54 std::string lock_name = | |
55 GetServiceProcessScopedName("_service_running"); | |
56 return TakeNamedLock(lock_name, waiting); | |
57 } | |
58 | |
59 MultiProcessLock* TakeServiceInitializingLock(bool waiting) { | |
60 std::string lock_name = | |
61 GetServiceProcessScopedName("_service_initializing"); | |
62 return TakeNamedLock(lock_name, waiting); | |
63 } | |
64 | |
65 // Watches for |kShutDownMessage| to be written to the file descriptor it is | |
66 // watching. When it reads |kShutDownMessage|, it performs |shutdown_task_|. | |
67 // Used here to monitor the socket listening to g_signal_socket. | |
68 class ServiceProcessShutdownMonitor | |
69 : public base::MessagePumpLibevent::Watcher { | |
70 public: | |
71 | |
72 enum { | |
73 kShutDownMessage = 0xdecea5e | |
74 }; | |
75 | |
76 explicit ServiceProcessShutdownMonitor(Task* shutdown_task) | |
77 : shutdown_task_(shutdown_task) { | |
78 } | |
79 | |
80 virtual ~ServiceProcessShutdownMonitor(); | |
81 | |
82 virtual void OnFileCanReadWithoutBlocking(int fd); | |
83 virtual void OnFileCanWriteWithoutBlocking(int fd); | |
84 | |
85 private: | |
86 scoped_ptr<Task> shutdown_task_; | |
87 }; | |
88 | |
89 ServiceProcessShutdownMonitor::~ServiceProcessShutdownMonitor() { | |
90 } | |
91 | |
92 void ServiceProcessShutdownMonitor::OnFileCanReadWithoutBlocking(int fd) { | |
93 if (shutdown_task_.get()) { | |
94 int buffer; | |
95 int length = read(fd, &buffer, sizeof(buffer)); | |
96 if ((length == sizeof(kShutDownMessage)) && (buffer == kShutDownMessage)) { | |
Scott Byer
2011/02/02 00:12:38
Should be sizeof(buffer) to match the write side.
dmac
2011/02/02 00:56:25
Done.
| |
97 shutdown_task_->Run(); | |
98 shutdown_task_.reset(); | |
99 } else if (length > 0) { | |
100 LOG(ERROR) << "Unexpected read: " << buffer; | |
101 } else if (length == 0) { | |
102 LOG(ERROR) << "Unexpected fd close"; | |
103 } else if (length < 0) { | |
104 PLOG(ERROR) << "read"; | |
105 } | |
106 } | |
107 } | |
108 | |
109 void ServiceProcessShutdownMonitor::OnFileCanWriteWithoutBlocking(int fd) { | |
110 NOTIMPLEMENTED(); | |
111 } | |
112 | |
113 // "Forced" Shutdowns on POSIX are done via signals. The magic signal for | |
114 // a shutdown is SIGTERM. "write" is a signal safe function. PLOG(ERROR) is | |
115 // not, but we don't ever expect it to be called. | |
116 void SigTermHandler(int sig, siginfo_t* info, void* uap) { | |
117 // TODO(dmaclach): add security here to make sure that we are being shut | |
118 // down by an appropriate process. | |
119 int message = ServiceProcessShutdownMonitor::kShutDownMessage; | |
120 if (write(g_signal_socket, &message, sizeof(message)) < 0) { | |
121 PLOG(ERROR) << "write"; | |
122 } | |
123 } | |
124 | |
24 } // namespace | 125 } // namespace |
25 | 126 |
26 bool ForceServiceProcessShutdown(const std::string& version) { | 127 // See comment for SigTermHandler. |
27 NOTIMPLEMENTED(); | 128 bool ForceServiceProcessShutdown(const std::string& version, |
28 return false; | 129 base::ProcessId process_id) { |
130 if (kill(process_id, SIGTERM) < 0) { | |
131 PLOG(ERROR) << "kill"; | |
132 return false; | |
133 } | |
134 return true; | |
29 } | 135 } |
30 | 136 |
31 bool CheckServiceProcessReady() { | 137 bool CheckServiceProcessReady() { |
32 const FilePath path = GetServiceProcessLockFilePath(); | 138 scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false)); |
33 return file_util::PathExists(path); | 139 return running_lock.get() == NULL; |
34 } | 140 } |
35 | 141 |
36 struct ServiceProcessState::StateData { | 142 struct ServiceProcessState::StateData |
37 // No state yet for Posix. | 143 : public base::RefCountedThreadSafe<ServiceProcessState::StateData> { |
144 scoped_ptr<MultiProcessLock> initializing_lock_; | |
145 scoped_ptr<MultiProcessLock> running_lock_; | |
146 scoped_ptr<ServiceProcessShutdownMonitor> shut_down_monitor_; | |
147 base::MessagePumpLibevent::FileDescriptorWatcher watcher_; | |
148 int sockets_[2]; | |
149 struct sigaction old_action_; | |
150 bool set_action_; | |
151 | |
152 // WatchFileDescriptor needs to be set up by the thread that is going | |
153 // to be monitoring it. | |
154 void SignalReady() { | |
155 CHECK(MessageLoopForIO::current()->WatchFileDescriptor( | |
156 sockets_[0], true, MessageLoopForIO::WATCH_READ, | |
157 &watcher_, shut_down_monitor_.get())); | |
158 g_signal_socket = sockets_[1]; | |
159 | |
160 // Set up signal handler for SIGTERM. | |
161 struct sigaction action; | |
162 action.sa_sigaction = SigTermHandler; | |
163 sigemptyset(&action.sa_mask); | |
164 action.sa_flags = SA_SIGINFO; | |
165 if (sigaction(SIGTERM, &action, &old_action_) == 0) { | |
166 // If the old_action is not default, somebody else has installed a | |
167 // a competing handler. Our handler is going to override it so it | |
168 // won't be called. If this occurs it needs to be fixed. | |
169 DCHECK_EQ(old_action_.sa_handler, SIG_DFL); | |
170 set_action_ = true; | |
171 initializing_lock_.reset(); | |
172 } else { | |
173 PLOG(ERROR) << "sigaction"; | |
174 } | |
175 } | |
38 }; | 176 }; |
39 | 177 |
40 bool ServiceProcessState::TakeSingletonLock() { | 178 bool ServiceProcessState::TakeSingletonLock() { |
41 // TODO(sanjeevr): Implement singleton mechanism for POSIX. | 179 CHECK(!state_); |
42 NOTIMPLEMENTED(); | 180 state_ = new StateData; |
181 state_->AddRef(); | |
182 state_->sockets_[0] = -1; | |
183 state_->sockets_[1] = -1; | |
184 state_->set_action_ = false; | |
185 state_->initializing_lock_.reset(TakeServiceInitializingLock(true)); | |
186 return state_->initializing_lock_.get(); | |
187 } | |
188 | |
189 bool ServiceProcessState::SignalReady(MessageLoop* message_loop, | |
190 Task* shutdown_task) { | |
191 CHECK(state_); | |
192 CHECK_EQ(g_signal_socket, -1); | |
193 DCHECK_EQ(message_loop->type(), MessageLoop::TYPE_IO); | |
194 | |
195 state_->running_lock_.reset(TakeServiceRunningLock(true)); | |
196 if (state_->running_lock_.get() == NULL) { | |
197 return false; | |
198 } | |
199 state_->shut_down_monitor_.reset( | |
200 new ServiceProcessShutdownMonitor(shutdown_task)); | |
201 if (pipe(state_->sockets_) < 0) { | |
202 PLOG(ERROR) << "pipe"; | |
203 return false; | |
204 } | |
205 message_loop->PostTask(FROM_HERE, | |
206 NewRunnableMethod(state_, &ServiceProcessState::StateData::SignalReady)); | |
43 return true; | 207 return true; |
44 } | 208 } |
45 | 209 |
46 void ServiceProcessState::SignalReady(Task* shutdown_task) { | |
47 // TODO(hclam): Implement better mechanism for these platform. | |
48 // Also we need to save shutdown task. For now we just delete the shutdown | |
49 // task because we have not way to listen for shutdown requests. | |
50 delete shutdown_task; | |
51 const FilePath path = GetServiceProcessLockFilePath(); | |
52 FILE* file = file_util::OpenFile(path, "wb+"); | |
53 if (!file) | |
54 return; | |
55 VLOG(1) << "Created Service Process lock file: " << path.value(); | |
56 file_util::TruncateFile(file) && file_util::CloseFile(file); | |
57 } | |
58 | |
59 void ServiceProcessState::SignalStopped() { | |
60 const FilePath path = GetServiceProcessLockFilePath(); | |
61 file_util::Delete(path, false); | |
62 shared_mem_service_data_.reset(); | |
63 } | |
64 | |
65 bool ServiceProcessState::AddToAutoRun() { | 210 bool ServiceProcessState::AddToAutoRun() { |
66 NOTIMPLEMENTED(); | 211 NOTIMPLEMENTED(); |
67 return false; | 212 return false; |
68 } | 213 } |
69 | 214 |
70 bool ServiceProcessState::RemoveFromAutoRun() { | 215 bool ServiceProcessState::RemoveFromAutoRun() { |
71 NOTIMPLEMENTED(); | 216 NOTIMPLEMENTED(); |
72 return false; | 217 return false; |
73 } | 218 } |
74 | 219 |
75 void ServiceProcessState::TearDownState() { | 220 void ServiceProcessState::TearDownState() { |
221 g_signal_socket = -1; | |
222 if (state_) { | |
223 if (state_->sockets_[0] != -1) { | |
224 close(state_->sockets_[0]); | |
225 } | |
226 if (state_->sockets_[1] != -1) { | |
227 close(state_->sockets_[1]); | |
228 } | |
229 if (state_->set_action_) { | |
230 if (sigaction(SIGTERM, &state_->old_action_, NULL) < 0) { | |
231 PLOG(ERROR) << "sigaction"; | |
232 } | |
233 } | |
234 state_->Release(); | |
235 state_ = NULL; | |
236 } | |
76 } | 237 } |
77 | 238 |
78 bool ServiceProcessState::ShouldHandleOtherVersion() { | 239 bool ServiceProcessState::ShouldHandleOtherVersion() { |
79 // On POSIX, the shared memory is a file in disk. We may have a stale file | 240 scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false)); |
80 // lying around from a previous run. So the check is not reliable. | 241 return running_lock.get() == NULL; |
Scott Byer
2011/02/02 00:12:38
Shouldn't this just call CheckServiceProcessReady(
dmac
2011/02/02 00:56:25
Got rid of it instead.
| |
81 return false; | |
82 } | 242 } |
83 | |
OLD | NEW |