OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "mojo/edk/system/child_broker_host.h" | 5 #include "mojo/edk/system/child_broker_host.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/lazy_instance.h" | 12 #include "base/lazy_instance.h" |
13 #include "mojo/edk/embedder/embedder_internal.h" | 13 #include "mojo/edk/embedder/embedder_internal.h" |
14 #include "mojo/edk/embedder/platform_channel_pair.h" | 14 #include "mojo/edk/embedder/platform_channel_pair.h" |
| 15 #include "mojo/edk/embedder/platform_shared_buffer.h" |
| 16 #include "mojo/edk/embedder/platform_support.h" |
15 #include "mojo/edk/system/broker_messages.h" | 17 #include "mojo/edk/system/broker_messages.h" |
16 #include "mojo/edk/system/broker_state.h" | 18 #include "mojo/edk/system/broker_state.h" |
17 #include "mojo/edk/system/configuration.h" | 19 #include "mojo/edk/system/configuration.h" |
18 #include "mojo/edk/system/core.h" | 20 #include "mojo/edk/system/core.h" |
19 #include "mojo/edk/system/platform_handle_dispatcher.h" | 21 #include "mojo/edk/system/platform_handle_dispatcher.h" |
20 | 22 |
| 23 #if defined(OS_POSIX) |
| 24 #include <fcntl.h> |
| 25 #include <sys/uio.h> |
| 26 |
| 27 #include "mojo/edk/embedder/platform_channel_utils_posix.h" |
| 28 #endif |
| 29 |
21 namespace mojo { | 30 namespace mojo { |
22 namespace edk { | 31 namespace edk { |
23 | 32 |
24 namespace { | 33 namespace { |
25 #if defined(OS_WIN) | 34 #if defined(OS_WIN) |
26 static const int kDefaultReadBufferSize = 256; | 35 static const int kDefaultReadBufferSize = 256; |
27 #endif | 36 #endif |
28 } | 37 } |
29 | 38 |
30 ChildBrokerHost::ChildBrokerHost(base::ProcessHandle child_process, | 39 ChildBrokerHost::ChildBrokerHost(base::ProcessHandle child_process, |
31 ScopedPlatformHandle pipe) | 40 ScopedPlatformHandle pipe) |
32 : process_id_(base::GetProcId(child_process)), child_channel_(nullptr) { | 41 : process_id_(base::GetProcId(child_process)), |
33 ScopedPlatformHandle parent_async_channel_handle; | 42 child_channel_(nullptr), |
34 #if defined(OS_POSIX) | 43 num_bytes_read_(0) { |
35 parent_async_channel_handle = std::move(pipe); | 44 // First set up the synchronous pipe. |
36 #else | 45 sync_channel_ = std::move(pipe); |
| 46 |
| 47 // See comment in ChildBroker::SetChildBrokerHostHandle. Summary is we need |
| 48 // two pipes, so send the second one over the first one. |
| 49 PlatformChannelPair parent_pipe; |
| 50 |
| 51 ScopedPlatformHandle parent_async_channel_handle = |
| 52 parent_pipe.PassServerHandle(); |
| 53 |
| 54 num_bytes_read_ = 0; |
| 55 |
| 56 // Send over the async pipe. |
| 57 #if defined(OS_WIN) |
37 DuplicateHandle(GetCurrentProcess(), child_process, | 58 DuplicateHandle(GetCurrentProcess(), child_process, |
38 GetCurrentProcess(), &child_process, | 59 GetCurrentProcess(), &child_process, |
39 0, FALSE, DUPLICATE_SAME_ACCESS); | 60 0, FALSE, DUPLICATE_SAME_ACCESS); |
40 child_process_ = base::Process(child_process); | 61 child_process_ = base::Process(child_process); |
41 sync_channel_ = pipe.Pass(); | 62 |
42 memset(&read_context_.overlapped, 0, sizeof(read_context_.overlapped)); | 63 memset(&read_context_.overlapped, 0, sizeof(read_context_.overlapped)); |
43 read_context_.handler = this; | 64 read_context_.handler = this; |
44 memset(&write_context_.overlapped, 0, sizeof(write_context_.overlapped)); | 65 memset(&write_context_.overlapped, 0, sizeof(write_context_.overlapped)); |
45 write_context_.handler = this; | 66 write_context_.handler = this; |
46 read_data_.resize(kDefaultReadBufferSize); | 67 read_data_.resize(kDefaultReadBufferSize); |
47 num_bytes_read_ = 0; | |
48 | |
49 // See comment in ChildBroker::SetChildBrokerHostHandle. Summary is we need | |
50 // two pipes on Windows, so send the second one over the first one. | |
51 PlatformChannelPair parent_pipe; | |
52 parent_async_channel_handle = parent_pipe.PassServerHandle(); | |
53 | 68 |
54 HANDLE duplicated_child_handle = | 69 HANDLE duplicated_child_handle = |
55 DuplicateToChild(parent_pipe.PassClientHandle().release().handle); | 70 DuplicateToChild(parent_pipe.PassClientHandle().release().handle); |
56 BOOL rv = WriteFile(sync_channel_.get().handle, | 71 BOOL rv = WriteFile(sync_channel_.get().handle, |
57 &duplicated_child_handle, sizeof(duplicated_child_handle), | 72 &duplicated_child_handle, sizeof(duplicated_child_handle), |
58 NULL, &write_context_.overlapped); | 73 NULL, &write_context_.overlapped); |
59 DCHECK(rv || GetLastError() == ERROR_IO_PENDING); | 74 DCHECK(rv || GetLastError() == ERROR_IO_PENDING); |
| 75 #else |
| 76 // Send just one null byte. |
| 77 struct iovec iov = {const_cast<char*>(""), 1}; |
| 78 PlatformHandle child_handle = parent_pipe.PassClientHandle().release(); |
| 79 ssize_t result = PlatformChannelSendmsgWithHandles(sync_channel_.get(), &iov, |
| 80 1, &child_handle, 1); |
| 81 CHECK_NE(-1, result); |
60 #endif | 82 #endif |
61 | 83 |
62 internal::g_io_thread_task_runner->PostTask( | 84 internal::g_io_thread_task_runner->PostTask( |
63 FROM_HERE, | 85 FROM_HERE, |
64 base::Bind(&ChildBrokerHost::InitOnIO, base::Unretained(this), | 86 base::Bind(&ChildBrokerHost::InitOnIO, base::Unretained(this), |
65 base::Passed(&parent_async_channel_handle))); | 87 base::Passed(&parent_async_channel_handle))); |
66 } | 88 } |
67 | 89 |
68 base::ProcessId ChildBrokerHost::GetProcessId() { | 90 base::ProcessId ChildBrokerHost::GetProcessId() { |
69 return process_id_; | 91 return process_id_; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 | 156 |
135 BrokerState::GetInstance()->ChildBrokerHostCreated(this); | 157 BrokerState::GetInstance()->ChildBrokerHostCreated(this); |
136 | 158 |
137 #if defined(OS_WIN) | 159 #if defined(OS_WIN) |
138 // Call this after RoutedRawChannel calls its RawChannel::Init because this | 160 // Call this after RoutedRawChannel calls its RawChannel::Init because this |
139 // call could cause us to get notified that the child has gone away and to | 161 // call could cause us to get notified that the child has gone away and to |
140 // delete this class and shut down child_channel_ before Init() has run. | 162 // delete this class and shut down child_channel_ before Init() has run. |
141 base::MessageLoopForIO::current()->RegisterIOHandler( | 163 base::MessageLoopForIO::current()->RegisterIOHandler( |
142 sync_channel_.get().handle, this); | 164 sync_channel_.get().handle, this); |
143 BeginRead(); | 165 BeginRead(); |
| 166 #else |
| 167 base::MessageLoopForIO::current()->WatchFileDescriptor( |
| 168 sync_channel_.get().handle, true, base::MessageLoopForIO::WATCH_READ, |
| 169 &fd_controller_, this); |
| 170 TryReadAndWriteHandles(); |
144 #endif | 171 #endif |
145 } | 172 } |
146 | 173 |
147 void ChildBrokerHost::OnReadMessage( | 174 void ChildBrokerHost::OnReadMessage( |
148 const MessageInTransit::View& message_view, | 175 const MessageInTransit::View& message_view, |
149 ScopedPlatformHandleVectorPtr platform_handles) { | 176 ScopedPlatformHandleVectorPtr platform_handles) { |
150 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | 177 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); |
151 CHECK(!platform_handles); | 178 CHECK(!platform_handles); |
152 if (message_view.num_bytes() != | 179 if (message_view.num_bytes() != |
153 static_cast<uint32_t>(sizeof(ConnectMessagePipeMessage))) { | 180 static_cast<uint32_t>(sizeof(ConnectMessagePipeMessage))) { |
(...skipping 18 matching lines...) Expand all Loading... |
172 } | 199 } |
173 } | 200 } |
174 | 201 |
175 void ChildBrokerHost::OnError(Error error) { | 202 void ChildBrokerHost::OnError(Error error) { |
176 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | 203 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); |
177 child_channel_->RemoveRoute(kBrokerRouteId); | 204 child_channel_->RemoveRoute(kBrokerRouteId); |
178 child_channel_ = nullptr; | 205 child_channel_ = nullptr; |
179 } | 206 } |
180 | 207 |
181 void ChildBrokerHost::ChannelDestructed(RoutedRawChannel* channel) { | 208 void ChildBrokerHost::ChannelDestructed(RoutedRawChannel* channel) { |
182 // On Windows, we have two pipes to the child process. It's easier to wait | 209 // We have two pipes to the child process. It's easier to wait |
183 // until we get the error from the pipe that is used for synchronous I/O. | 210 // until we get the error from the pipe that is used for synchronous I/O. |
184 #if !defined(OS_WIN) | |
185 delete this; | |
186 #endif | |
187 } | 211 } |
188 | 212 |
189 #if defined(OS_WIN) | 213 #if defined(OS_WIN) |
190 void ChildBrokerHost::BeginRead() { | 214 void ChildBrokerHost::BeginRead() { |
191 BOOL rv = ReadFile(sync_channel_.get().handle, | 215 BOOL rv = ReadFile(sync_channel_.get().handle, |
192 &read_data_[num_bytes_read_], | 216 &read_data_[num_bytes_read_], |
193 static_cast<int>(read_data_.size() - num_bytes_read_), | 217 static_cast<int>(read_data_.size() - num_bytes_read_), |
194 nullptr, &read_context_.overlapped); | 218 nullptr, &read_context_.overlapped); |
195 if (rv || GetLastError() == ERROR_IO_PENDING) | 219 if (rv || GetLastError() == ERROR_IO_PENDING) |
196 return; | 220 return; |
(...skipping 21 matching lines...) Expand all Loading... |
218 return; | 242 return; |
219 } | 243 } |
220 | 244 |
221 if (context == &write_context_) { | 245 if (context == &write_context_) { |
222 write_data_.clear(); | 246 write_data_.clear(); |
223 return; | 247 return; |
224 } | 248 } |
225 | 249 |
226 num_bytes_read_ += bytes_transferred; | 250 num_bytes_read_ += bytes_transferred; |
227 CHECK_GE(num_bytes_read_, sizeof(uint32_t)); | 251 CHECK_GE(num_bytes_read_, sizeof(uint32_t)); |
228 BrokerMessage* message = reinterpret_cast<BrokerMessage*>(&read_data_[0]); | 252 BrokerMessage* message = reinterpret_cast<BrokerMessage*>(read_data_.data()); |
229 if (num_bytes_read_ < message->size) { | 253 if (num_bytes_read_ < message->size) { |
230 read_data_.resize(message->size); | 254 read_data_.resize(message->size); |
231 BeginRead(); | 255 BeginRead(); |
232 return; | 256 return; |
233 } | 257 } |
234 | 258 |
235 // This should never fire because we only get new requests from a child | 259 // This should never fire because we only get new requests from a child |
236 // process after it has read all the previous data we wrote. | 260 // process after it has read all the previous data we wrote. |
237 if (!write_data_.empty()) { | 261 if (!write_data_.empty()) { |
238 NOTREACHED() << "ChildBrokerHost shouldn't have data to write when it gets " | 262 NOTREACHED() << "ChildBrokerHost shouldn't have data to write when it gets " |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 } | 340 } |
317 | 341 |
318 HANDLE ChildBrokerHost::DuplicateFromChild(HANDLE handle) { | 342 HANDLE ChildBrokerHost::DuplicateFromChild(HANDLE handle) { |
319 HANDLE rv = INVALID_HANDLE_VALUE; | 343 HANDLE rv = INVALID_HANDLE_VALUE; |
320 BOOL result = DuplicateHandle(child_process_.Handle(), handle, | 344 BOOL result = DuplicateHandle(child_process_.Handle(), handle, |
321 base::GetCurrentProcessHandle(), &rv, 0, FALSE, | 345 base::GetCurrentProcessHandle(), &rv, 0, FALSE, |
322 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); | 346 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); |
323 DCHECK(result); | 347 DCHECK(result); |
324 return rv; | 348 return rv; |
325 } | 349 } |
| 350 #else |
| 351 void ChildBrokerHost::TryReadAndWriteHandles() { |
| 352 // Message sizes are constant on POSIX currently. Further, the child process |
| 353 // only writes one message before it gets a response. |
| 354 read_data_.resize(sizeof(BrokerMessage)); |
| 355 |
| 356 std::deque<PlatformHandle> dummy; |
| 357 ssize_t bytes_read = PlatformChannelRecvmsg( |
| 358 sync_channel_.get(), &read_data_[num_bytes_read_], |
| 359 static_cast<int>(read_data_.size() - num_bytes_read_), &dummy); |
| 360 DCHECK(dummy.empty()); |
| 361 |
| 362 // We call TryReadAndWriteHandles when we are first initialized so this could |
| 363 // fail with EAGAIN or EWOULDBLOCK. |
| 364 if (bytes_read == 0 || |
| 365 (bytes_read == -1 && errno != EAGAIN && errno != EWOULDBLOCK)) { |
| 366 delete this; |
| 367 return; |
| 368 } |
| 369 |
| 370 if (bytes_read == -1) |
| 371 return; |
| 372 |
| 373 num_bytes_read_ += bytes_read; |
| 374 |
| 375 // We don't know how big the message is yet. |
| 376 if (num_bytes_read_ < sizeof(uint32_t)) |
| 377 return; |
| 378 |
| 379 BrokerMessage* message = reinterpret_cast<BrokerMessage*>(read_data_.data()); |
| 380 // Message not fully read yet. |
| 381 if (num_bytes_read_ < message->size) { |
| 382 DCHECK_LE(message->size, sizeof(BrokerMessage)); |
| 383 return; |
| 384 } |
| 385 |
| 386 // Should only get one message. |
| 387 DCHECK_EQ(num_bytes_read_, message->size); |
| 388 |
| 389 PlatformHandle handle; |
| 390 if (message->id == CREATE_SHARED_BUFFER) { |
| 391 scoped_refptr<PlatformSharedBuffer> shared_buffer = |
| 392 internal::g_platform_support->CreateSharedBuffer( |
| 393 message->shared_buffer_size); |
| 394 if (shared_buffer) |
| 395 handle = shared_buffer->PassPlatformHandle().release(); |
| 396 else |
| 397 LOG(ERROR) << "ChildBrokerHost failed to create shared buffer of size " |
| 398 << message->shared_buffer_size; |
| 399 } else { |
| 400 NOTREACHED() << "Unknown command. Stopping reading."; |
| 401 delete this; |
| 402 return; |
| 403 } |
| 404 |
| 405 num_bytes_read_ = 0; |
| 406 read_data_.clear(); |
| 407 |
| 408 // Send just one null byte. Also send back a null platform handle if we |
| 409 // couldn't create the shared buffer. |
| 410 struct iovec iov = {const_cast<char*>(""), 1}; |
| 411 ssize_t result = PlatformChannelSendmsgWithHandles(sync_channel_.get(), &iov, |
| 412 1, &handle, 1); |
| 413 if (result == -1) { |
| 414 PLOG(ERROR) << "ChildBrokerHost could not write to peer"; |
| 415 delete this; |
| 416 } |
| 417 } |
| 418 |
| 419 void ChildBrokerHost::OnFileCanReadWithoutBlocking(int fd) { |
| 420 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); |
| 421 if (fd != sync_channel_.get().handle) { |
| 422 NOTREACHED() << "ChildBrokerHost shouldn't get notifications about file " |
| 423 "descriptors other than sync_channel_'s"; |
| 424 delete this; |
| 425 return; |
| 426 } |
| 427 |
| 428 TryReadAndWriteHandles(); |
| 429 } |
| 430 |
| 431 void ChildBrokerHost::OnFileCanWriteWithoutBlocking(int fd) {} |
326 #endif | 432 #endif |
327 | 433 |
328 } // namespace edk | 434 } // namespace edk |
329 } // namespace mojo | 435 } // namespace mojo |
OLD | NEW |