OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "mojo/edk/system/child_broker.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <stdint.h> | |
9 | |
10 #include <utility> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/logging.h" | |
14 #include "mojo/edk/embedder/embedder_internal.h" | |
15 #include "mojo/edk/embedder/platform_channel_pair.h" | |
16 #include "mojo/edk/system/broker_messages.h" | |
17 #include "mojo/edk/system/message_pipe_dispatcher.h" | |
18 | |
19 namespace mojo { | |
20 namespace edk { | |
21 | |
22 ChildBroker* ChildBroker::GetInstance() { | |
23 return base::Singleton< | |
24 ChildBroker, base::LeakySingletonTraits<ChildBroker>>::get(); | |
25 } | |
26 | |
27 void ChildBroker::SetChildBrokerHostHandle(ScopedPlatformHandle handle) { | |
28 ScopedPlatformHandle parent_async_channel_handle; | |
29 #if defined(OS_POSIX) | |
30 parent_async_channel_handle = std::move(handle); | |
31 #else | |
32 // On Windows we have two pipes to the parent. The first is for the token | |
33 // exchange for creating and passing handles, since the child needs the | |
34 // parent's help if it is sandboxed. The second is the same as POSIX, which is | |
35 // used for multiplexing related messages. So on Windows, we send the second | |
36 // pipe as the first string over the first one. | |
37 parent_sync_channel_ = std::move(handle); | |
38 | |
39 HANDLE parent_handle = INVALID_HANDLE_VALUE; | |
40 DWORD bytes_read = 0; | |
41 BOOL rv = ReadFile(parent_sync_channel_.get().handle, &parent_handle, | |
42 sizeof(parent_handle), &bytes_read, NULL); | |
43 CHECK(rv); | |
44 parent_async_channel_handle.reset(PlatformHandle(parent_handle)); | |
45 sync_channel_lock_.Unlock(); | |
46 #endif | |
47 | |
48 internal::g_io_thread_task_runner->PostTask( | |
49 FROM_HERE, | |
50 base::Bind(&ChildBroker::InitAsyncChannel, base::Unretained(this), | |
51 base::Passed(&parent_async_channel_handle))); | |
52 } | |
53 | |
54 #if defined(OS_WIN) | |
55 void ChildBroker::CreatePlatformChannelPair( | |
56 ScopedPlatformHandle* server, ScopedPlatformHandle* client) { | |
57 sync_channel_lock_.Lock(); | |
58 CreatePlatformChannelPairNoLock(server, client); | |
59 sync_channel_lock_.Unlock(); | |
60 } | |
61 | |
62 void ChildBroker::HandleToToken(const PlatformHandle* platform_handles, | |
63 size_t count, | |
64 uint64_t* tokens) { | |
65 uint32_t size = kBrokerMessageHeaderSize + | |
66 static_cast<int>(count) * sizeof(HANDLE); | |
67 std::vector<char> message_buffer(size); | |
68 BrokerMessage* message = reinterpret_cast<BrokerMessage*>(&message_buffer[0]); | |
69 message->size = size; | |
70 message->id = HANDLE_TO_TOKEN; | |
71 for (size_t i = 0; i < count; ++i) | |
72 message->handles[i] = platform_handles[i].handle; | |
73 | |
74 uint32_t response_size = static_cast<int>(count) * sizeof(uint64_t); | |
75 sync_channel_lock_.Lock(); | |
76 WriteAndReadResponse(message, tokens, response_size); | |
77 sync_channel_lock_.Unlock(); | |
78 } | |
79 | |
80 void ChildBroker::TokenToHandle(const uint64_t* tokens, | |
81 size_t count, | |
82 PlatformHandle* handles) { | |
83 uint32_t size = kBrokerMessageHeaderSize + | |
84 static_cast<int>(count) * sizeof(uint64_t); | |
85 std::vector<char> message_buffer(size); | |
86 BrokerMessage* message = | |
87 reinterpret_cast<BrokerMessage*>(&message_buffer[0]); | |
88 message->size = size; | |
89 message->id = TOKEN_TO_HANDLE; | |
90 memcpy(&message->tokens[0], tokens, count * sizeof(uint64_t)); | |
91 | |
92 std::vector<HANDLE> handles_temp(count); | |
93 uint32_t response_size = | |
94 static_cast<uint32_t>(handles_temp.size()) * sizeof(HANDLE); | |
95 sync_channel_lock_.Lock(); | |
96 if (WriteAndReadResponse(message, &handles_temp[0], response_size)) { | |
97 for (uint32_t i = 0; i < count; ++i) | |
98 handles[i].handle = handles_temp[i]; | |
99 sync_channel_lock_.Unlock(); | |
100 } | |
101 } | |
102 #endif | |
103 | |
104 void ChildBroker::ConnectMessagePipe(uint64_t pipe_id, | |
105 MessagePipeDispatcher* message_pipe) { | |
106 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
107 | |
108 ConnectMessagePipeMessage data; | |
109 memset(&data, 0, sizeof(data)); | |
110 data.pipe_id = pipe_id; | |
111 if (pending_connects_.find(pipe_id) != pending_connects_.end()) { | |
112 if (!parent_async_channel_) { | |
113 // On Windows, we can't create the local RoutedRawChannel yet because we | |
114 // don't have parent_sync_channel_. Treat all platforms the same and just | |
115 // queue this. | |
116 CHECK(pending_inprocess_connects_.find(pipe_id) == | |
117 pending_inprocess_connects_.end()); | |
118 pending_inprocess_connects_[pipe_id] = message_pipe; | |
119 return; | |
120 } | |
121 // Both ends of the message pipe are in the same process. | |
122 // First, tell the browser side that to remove its bookkeeping for a pending | |
123 // connect, since it'll never get the other side. | |
124 | |
125 data.type = CANCEL_CONNECT_MESSAGE_PIPE; | |
126 scoped_ptr<MessageInTransit> message(new MessageInTransit( | |
127 MessageInTransit::Type::MESSAGE, sizeof(data), &data)); | |
128 WriteAsyncMessage(std::move(message)); | |
129 | |
130 if (!in_process_pipes_channel1_) { | |
131 ScopedPlatformHandle server_handle, client_handle; | |
132 #if defined(OS_WIN) | |
133 CreatePlatformChannelPairNoLock(&server_handle, &client_handle); | |
134 #else | |
135 PlatformChannelPair channel_pair; | |
136 server_handle = channel_pair.PassServerHandle(); | |
137 client_handle = channel_pair.PassClientHandle(); | |
138 #endif | |
139 in_process_pipes_channel1_ = new RoutedRawChannel( | |
140 std::move(server_handle), | |
141 base::Bind(&ChildBroker::ChannelDestructed, base::Unretained(this))); | |
142 in_process_pipes_channel2_ = new RoutedRawChannel( | |
143 std::move(client_handle), | |
144 base::Bind(&ChildBroker::ChannelDestructed, base::Unretained(this))); | |
145 } | |
146 | |
147 AttachMessagePipe(pending_connects_[pipe_id], pipe_id, | |
148 in_process_pipes_channel1_); | |
149 AttachMessagePipe(message_pipe, pipe_id, in_process_pipes_channel2_); | |
150 pending_connects_.erase(pipe_id); | |
151 return; | |
152 } | |
153 | |
154 data.type = CONNECT_MESSAGE_PIPE; | |
155 scoped_ptr<MessageInTransit> message(new MessageInTransit( | |
156 MessageInTransit::Type::MESSAGE, sizeof(data), &data)); | |
157 pending_connects_[pipe_id] = message_pipe; | |
158 WriteAsyncMessage(std::move(message)); | |
159 } | |
160 | |
161 void ChildBroker::CloseMessagePipe( | |
162 uint64_t pipe_id, MessagePipeDispatcher* message_pipe) { | |
163 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
164 CHECK(connected_pipes_.find(message_pipe) != connected_pipes_.end()); | |
165 connected_pipes_[message_pipe]->RemoveRoute(pipe_id); | |
166 connected_pipes_.erase(message_pipe); | |
167 } | |
168 | |
169 ChildBroker::ChildBroker() | |
170 : parent_async_channel_(nullptr), | |
171 in_process_pipes_channel1_(nullptr), | |
172 in_process_pipes_channel2_(nullptr) { | |
173 DCHECK(!internal::g_broker); | |
174 internal::g_broker = this; | |
175 #if defined(OS_WIN) | |
176 // Block any threads from calling this until we have a pipe to the parent. | |
177 sync_channel_lock_.Lock(); | |
178 #endif | |
179 } | |
180 | |
181 ChildBroker::~ChildBroker() { | |
182 } | |
183 | |
184 void ChildBroker::OnReadMessage( | |
185 const MessageInTransit::View& message_view, | |
186 ScopedPlatformHandleVectorPtr platform_handles) { | |
187 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
188 MultiplexMessages type = | |
189 *static_cast<const MultiplexMessages*>(message_view.bytes()); | |
190 if (type == CONNECT_TO_PROCESS) { | |
191 DCHECK_EQ(platform_handles->size(), 1u); | |
192 ScopedPlatformHandle handle((*platform_handles.get())[0]); | |
193 (*platform_handles.get())[0] = PlatformHandle(); | |
194 | |
195 const ConnectToProcessMessage* message = | |
196 static_cast<const ConnectToProcessMessage*>(message_view.bytes()); | |
197 | |
198 CHECK(channels_.find(message->process_id) == channels_.end()); | |
199 channels_[message->process_id] = new RoutedRawChannel( | |
200 std::move(handle), | |
201 base::Bind(&ChildBroker::ChannelDestructed, base::Unretained(this))); | |
202 } else if (type == PEER_PIPE_CONNECTED) { | |
203 DCHECK(!platform_handles); | |
204 const PeerPipeConnectedMessage* message = | |
205 static_cast<const PeerPipeConnectedMessage*>(message_view.bytes()); | |
206 | |
207 uint64_t pipe_id = message->pipe_id; | |
208 uint64_t peer_pid = message->process_id; | |
209 | |
210 CHECK(pending_connects_.find(pipe_id) != pending_connects_.end()); | |
211 MessagePipeDispatcher* pipe = pending_connects_[pipe_id]; | |
212 pending_connects_.erase(pipe_id); | |
213 if (peer_pid == 0) { | |
214 // The other side is in the parent process. | |
215 AttachMessagePipe(pipe, pipe_id, parent_async_channel_); | |
216 } else if (channels_.find(peer_pid) == channels_.end()) { | |
217 // We saw the peer process die before we got the reply from the parent. | |
218 pipe->OnError(ERROR_READ_SHUTDOWN); | |
219 } else { | |
220 CHECK(connected_pipes_.find(pipe) == connected_pipes_.end()); | |
221 AttachMessagePipe(pipe, pipe_id, channels_[peer_pid]); | |
222 } | |
223 } else if (type == PEER_DIED) { | |
224 DCHECK(!platform_handles); | |
225 const PeerDiedMessage* message = | |
226 static_cast<const PeerDiedMessage*>(message_view.bytes()); | |
227 | |
228 uint64_t pipe_id = message->pipe_id; | |
229 | |
230 CHECK(pending_connects_.find(pipe_id) != pending_connects_.end()); | |
231 MessagePipeDispatcher* pipe = pending_connects_[pipe_id]; | |
232 pending_connects_.erase(pipe_id); | |
233 pipe->OnError(ERROR_READ_SHUTDOWN); | |
234 } else { | |
235 NOTREACHED(); | |
236 } | |
237 } | |
238 | |
239 void ChildBroker::OnError(Error error) { | |
240 // The parent process shut down. | |
241 } | |
242 | |
243 void ChildBroker::ChannelDestructed(RoutedRawChannel* channel) { | |
244 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
245 for (auto it : channels_) { | |
246 if (it.second == channel) { | |
247 channels_.erase(it.first); | |
248 break; | |
249 } | |
250 } | |
251 } | |
252 | |
253 void ChildBroker::WriteAsyncMessage(scoped_ptr<MessageInTransit> message) { | |
254 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
255 message->set_route_id(kBrokerRouteId); | |
256 if (parent_async_channel_) { | |
257 parent_async_channel_->channel()->WriteMessage(std::move(message)); | |
258 } else { | |
259 async_channel_queue_.AddMessage(std::move(message)); | |
260 } | |
261 } | |
262 | |
263 void ChildBroker::InitAsyncChannel( | |
264 ScopedPlatformHandle parent_async_channel_handle) { | |
265 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
266 | |
267 parent_async_channel_ = new RoutedRawChannel( | |
268 std::move(parent_async_channel_handle), | |
269 base::Bind(&ChildBroker::ChannelDestructed, base::Unretained(this))); | |
270 parent_async_channel_->AddRoute(kBrokerRouteId, this); | |
271 while (!async_channel_queue_.IsEmpty()) { | |
272 parent_async_channel_->channel()->WriteMessage( | |
273 async_channel_queue_.GetMessage()); | |
274 } | |
275 | |
276 while (!pending_inprocess_connects_.empty()) { | |
277 ConnectMessagePipe(pending_inprocess_connects_.begin()->first, | |
278 pending_inprocess_connects_.begin()->second); | |
279 pending_inprocess_connects_.erase(pending_inprocess_connects_.begin()); | |
280 } | |
281 } | |
282 | |
283 void ChildBroker::AttachMessagePipe(MessagePipeDispatcher* message_pipe, | |
284 uint64_t pipe_id, | |
285 RoutedRawChannel* raw_channel) { | |
286 connected_pipes_[message_pipe] = raw_channel; | |
287 // Note: we must call GotNonTransferableChannel before AddRoute because there | |
288 // could be race conditions if the pipe got queued messages in |AddRoute| but | |
289 // then when it's read it returns no messages because it doesn't have the | |
290 // channel yet. | |
291 message_pipe->GotNonTransferableChannel(raw_channel->channel()); | |
292 // The above call could have caused |CloseMessagePipe| to be called. | |
293 if (connected_pipes_.find(message_pipe) != connected_pipes_.end()) | |
294 raw_channel->AddRoute(pipe_id, message_pipe); | |
295 } | |
296 | |
297 #if defined(OS_WIN) | |
298 | |
299 bool ChildBroker::WriteAndReadResponse(BrokerMessage* message, | |
300 void* response, | |
301 uint32_t response_size) { | |
302 CHECK(parent_sync_channel_.is_valid()); | |
303 | |
304 bool result = true; | |
305 DWORD bytes_written = 0; | |
306 // This will always write in one chunk per | |
307 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365150.aspx. | |
308 BOOL rv = WriteFile(parent_sync_channel_.get().handle, message, message->size, | |
309 &bytes_written, NULL); | |
310 if (!rv || bytes_written != message->size) { | |
311 LOG(ERROR) << "Child token serializer couldn't write message."; | |
312 result = false; | |
313 } else { | |
314 while (response_size) { | |
315 DWORD bytes_read = 0; | |
316 rv = ReadFile(parent_sync_channel_.get().handle, response, response_size, | |
317 &bytes_read, NULL); | |
318 if (!rv) { | |
319 LOG(ERROR) << "Child token serializer couldn't read result."; | |
320 result = false; | |
321 break; | |
322 } | |
323 response_size -= bytes_read; | |
324 response = static_cast<char*>(response) + bytes_read; | |
325 } | |
326 } | |
327 | |
328 return result; | |
329 } | |
330 | |
331 void ChildBroker::CreatePlatformChannelPairNoLock( | |
332 ScopedPlatformHandle* server, ScopedPlatformHandle* client) { | |
333 BrokerMessage message; | |
334 message.size = kBrokerMessageHeaderSize; | |
335 message.id = CREATE_PLATFORM_CHANNEL_PAIR; | |
336 | |
337 uint32_t response_size = 2 * sizeof(HANDLE); | |
338 HANDLE handles[2]; | |
339 if (WriteAndReadResponse(&message, handles, response_size)) { | |
340 server->reset(PlatformHandle(handles[0])); | |
341 client->reset(PlatformHandle(handles[1])); | |
342 } | |
343 } | |
344 | |
345 #endif | |
346 | |
347 } // namespace edk | |
348 } // namespace mojo | |
OLD | NEW |