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" | |
17 #include "mojo/edk/system/broker_messages.h" | 15 #include "mojo/edk/system/broker_messages.h" |
18 #include "mojo/edk/system/broker_state.h" | 16 #include "mojo/edk/system/broker_state.h" |
19 #include "mojo/edk/system/configuration.h" | 17 #include "mojo/edk/system/configuration.h" |
20 #include "mojo/edk/system/core.h" | 18 #include "mojo/edk/system/core.h" |
21 #include "mojo/edk/system/platform_handle_dispatcher.h" | 19 #include "mojo/edk/system/platform_handle_dispatcher.h" |
22 | 20 |
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 | |
30 namespace mojo { | 21 namespace mojo { |
31 namespace edk { | 22 namespace edk { |
32 | 23 |
33 namespace { | 24 namespace { |
34 #if defined(OS_WIN) | 25 #if defined(OS_WIN) |
35 static const int kDefaultReadBufferSize = 256; | 26 static const int kDefaultReadBufferSize = 256; |
36 #endif | 27 #endif |
37 } | 28 } |
38 | 29 |
39 ChildBrokerHost::ChildBrokerHost(base::ProcessHandle child_process, | 30 ChildBrokerHost::ChildBrokerHost(base::ProcessHandle child_process, |
40 ScopedPlatformHandle pipe) | 31 ScopedPlatformHandle pipe) |
41 : process_id_(base::GetProcId(child_process)), | 32 : process_id_(base::GetProcId(child_process)), child_channel_(nullptr) { |
42 child_channel_(nullptr), | 33 ScopedPlatformHandle parent_async_channel_handle; |
43 num_bytes_read_(0) { | 34 #if defined(OS_POSIX) |
44 // First set up the synchronous pipe. | 35 parent_async_channel_handle = std::move(pipe); |
45 sync_channel_ = std::move(pipe); | 36 #else |
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) | |
58 DuplicateHandle(GetCurrentProcess(), child_process, | 37 DuplicateHandle(GetCurrentProcess(), child_process, |
59 GetCurrentProcess(), &child_process, | 38 GetCurrentProcess(), &child_process, |
60 0, FALSE, DUPLICATE_SAME_ACCESS); | 39 0, FALSE, DUPLICATE_SAME_ACCESS); |
61 child_process_ = base::Process(child_process); | 40 child_process_ = base::Process(child_process); |
62 | 41 sync_channel_ = std::move(pipe); |
63 memset(&read_context_.overlapped, 0, sizeof(read_context_.overlapped)); | 42 memset(&read_context_.overlapped, 0, sizeof(read_context_.overlapped)); |
64 read_context_.handler = this; | 43 read_context_.handler = this; |
65 memset(&write_context_.overlapped, 0, sizeof(write_context_.overlapped)); | 44 memset(&write_context_.overlapped, 0, sizeof(write_context_.overlapped)); |
66 write_context_.handler = this; | 45 write_context_.handler = this; |
67 read_data_.resize(kDefaultReadBufferSize); | 46 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(); |
68 | 53 |
69 HANDLE duplicated_child_handle = | 54 HANDLE duplicated_child_handle = |
70 DuplicateToChild(parent_pipe.PassClientHandle().release().handle); | 55 DuplicateToChild(parent_pipe.PassClientHandle().release().handle); |
71 BOOL rv = WriteFile(sync_channel_.get().handle, | 56 BOOL rv = WriteFile(sync_channel_.get().handle, |
72 &duplicated_child_handle, sizeof(duplicated_child_handle), | 57 &duplicated_child_handle, sizeof(duplicated_child_handle), |
73 NULL, &write_context_.overlapped); | 58 NULL, &write_context_.overlapped); |
74 DCHECK(rv || GetLastError() == ERROR_IO_PENDING); | 59 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); | |
82 #endif | 60 #endif |
83 | 61 |
84 internal::g_io_thread_task_runner->PostTask( | 62 internal::g_io_thread_task_runner->PostTask( |
85 FROM_HERE, | 63 FROM_HERE, |
86 base::Bind(&ChildBrokerHost::InitOnIO, base::Unretained(this), | 64 base::Bind(&ChildBrokerHost::InitOnIO, base::Unretained(this), |
87 base::Passed(&parent_async_channel_handle))); | 65 base::Passed(&parent_async_channel_handle))); |
88 } | 66 } |
89 | 67 |
90 base::ProcessId ChildBrokerHost::GetProcessId() { | 68 base::ProcessId ChildBrokerHost::GetProcessId() { |
91 return process_id_; | 69 return process_id_; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
156 | 134 |
157 BrokerState::GetInstance()->ChildBrokerHostCreated(this); | 135 BrokerState::GetInstance()->ChildBrokerHostCreated(this); |
158 | 136 |
159 #if defined(OS_WIN) | 137 #if defined(OS_WIN) |
160 // Call this after RoutedRawChannel calls its RawChannel::Init because this | 138 // Call this after RoutedRawChannel calls its RawChannel::Init because this |
161 // call could cause us to get notified that the child has gone away and to | 139 // call could cause us to get notified that the child has gone away and to |
162 // delete this class and shut down child_channel_ before Init() has run. | 140 // delete this class and shut down child_channel_ before Init() has run. |
163 base::MessageLoopForIO::current()->RegisterIOHandler( | 141 base::MessageLoopForIO::current()->RegisterIOHandler( |
164 sync_channel_.get().handle, this); | 142 sync_channel_.get().handle, this); |
165 BeginRead(); | 143 BeginRead(); |
166 #else | |
167 base::MessageLoopForIO::current()->WatchFileDescriptor( | |
168 sync_channel_.get().handle, true, base::MessageLoopForIO::WATCH_READ, | |
169 &fd_controller_, this); | |
170 TryReadAndWriteHandles(); | |
171 #endif | 144 #endif |
172 } | 145 } |
173 | 146 |
174 void ChildBrokerHost::OnReadMessage( | 147 void ChildBrokerHost::OnReadMessage( |
175 const MessageInTransit::View& message_view, | 148 const MessageInTransit::View& message_view, |
176 ScopedPlatformHandleVectorPtr platform_handles) { | 149 ScopedPlatformHandleVectorPtr platform_handles) { |
177 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | 150 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); |
178 CHECK(!platform_handles); | 151 CHECK(!platform_handles); |
179 if (message_view.num_bytes() != | 152 if (message_view.num_bytes() != |
180 static_cast<uint32_t>(sizeof(ConnectMessagePipeMessage))) { | 153 static_cast<uint32_t>(sizeof(ConnectMessagePipeMessage))) { |
(...skipping 18 matching lines...) Expand all Loading... |
199 } | 172 } |
200 } | 173 } |
201 | 174 |
202 void ChildBrokerHost::OnError(Error error) { | 175 void ChildBrokerHost::OnError(Error error) { |
203 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | 176 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); |
204 child_channel_->RemoveRoute(kBrokerRouteId); | 177 child_channel_->RemoveRoute(kBrokerRouteId); |
205 child_channel_ = nullptr; | 178 child_channel_ = nullptr; |
206 } | 179 } |
207 | 180 |
208 void ChildBrokerHost::ChannelDestructed(RoutedRawChannel* channel) { | 181 void ChildBrokerHost::ChannelDestructed(RoutedRawChannel* channel) { |
209 // We have two pipes to the child process. It's easier to wait | 182 // On Windows, we have two pipes to the child process. It's easier to wait |
210 // until we get the error from the pipe that is used for synchronous I/O. | 183 // 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 |
211 } | 187 } |
212 | 188 |
213 #if defined(OS_WIN) | 189 #if defined(OS_WIN) |
214 void ChildBrokerHost::BeginRead() { | 190 void ChildBrokerHost::BeginRead() { |
215 BOOL rv = ReadFile(sync_channel_.get().handle, | 191 BOOL rv = ReadFile(sync_channel_.get().handle, |
216 &read_data_[num_bytes_read_], | 192 &read_data_[num_bytes_read_], |
217 static_cast<int>(read_data_.size() - num_bytes_read_), | 193 static_cast<int>(read_data_.size() - num_bytes_read_), |
218 nullptr, &read_context_.overlapped); | 194 nullptr, &read_context_.overlapped); |
219 if (rv || GetLastError() == ERROR_IO_PENDING) | 195 if (rv || GetLastError() == ERROR_IO_PENDING) |
220 return; | 196 return; |
(...skipping 21 matching lines...) Expand all Loading... |
242 return; | 218 return; |
243 } | 219 } |
244 | 220 |
245 if (context == &write_context_) { | 221 if (context == &write_context_) { |
246 write_data_.clear(); | 222 write_data_.clear(); |
247 return; | 223 return; |
248 } | 224 } |
249 | 225 |
250 num_bytes_read_ += bytes_transferred; | 226 num_bytes_read_ += bytes_transferred; |
251 CHECK_GE(num_bytes_read_, sizeof(uint32_t)); | 227 CHECK_GE(num_bytes_read_, sizeof(uint32_t)); |
252 BrokerMessage* message = reinterpret_cast<BrokerMessage*>(read_data_.data()); | 228 BrokerMessage* message = reinterpret_cast<BrokerMessage*>(&read_data_[0]); |
253 if (num_bytes_read_ < message->size) { | 229 if (num_bytes_read_ < message->size) { |
254 read_data_.resize(message->size); | 230 read_data_.resize(message->size); |
255 BeginRead(); | 231 BeginRead(); |
256 return; | 232 return; |
257 } | 233 } |
258 | 234 |
259 // This should never fire because we only get new requests from a child | 235 // This should never fire because we only get new requests from a child |
260 // process after it has read all the previous data we wrote. | 236 // process after it has read all the previous data we wrote. |
261 if (!write_data_.empty()) { | 237 if (!write_data_.empty()) { |
262 NOTREACHED() << "ChildBrokerHost shouldn't have data to write when it gets " | 238 NOTREACHED() << "ChildBrokerHost shouldn't have data to write when it gets " |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
340 } | 316 } |
341 | 317 |
342 HANDLE ChildBrokerHost::DuplicateFromChild(HANDLE handle) { | 318 HANDLE ChildBrokerHost::DuplicateFromChild(HANDLE handle) { |
343 HANDLE rv = INVALID_HANDLE_VALUE; | 319 HANDLE rv = INVALID_HANDLE_VALUE; |
344 BOOL result = DuplicateHandle(child_process_.Handle(), handle, | 320 BOOL result = DuplicateHandle(child_process_.Handle(), handle, |
345 base::GetCurrentProcessHandle(), &rv, 0, FALSE, | 321 base::GetCurrentProcessHandle(), &rv, 0, FALSE, |
346 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); | 322 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); |
347 DCHECK(result); | 323 DCHECK(result); |
348 return rv; | 324 return rv; |
349 } | 325 } |
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) {} | |
432 #endif | 326 #endif |
433 | 327 |
434 } // namespace edk | 328 } // namespace edk |
435 } // namespace mojo | 329 } // namespace mojo |
OLD | NEW |