| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "mojo/core/broker.h" |
| |
| #include <fcntl.h> |
| #include <unistd.h> |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/memory/platform_shared_memory_region.h" |
| #include "build/build_config.h" |
| #include "mojo/core/broker_messages.h" |
| #include "mojo/core/channel.h" |
| #include "mojo/core/platform_handle_utils.h" |
| #include "mojo/public/cpp/platform/socket_utils_posix.h" |
| |
| namespace mojo { |
| namespace core { |
| |
| namespace { |
| |
| Channel::MessagePtr WaitForBrokerMessage( |
| int socket_fd, |
| BrokerMessageType expected_type, |
| size_t expected_num_handles, |
| size_t expected_data_size, |
| std::vector<PlatformHandle>* incoming_handles) { |
| Channel::MessagePtr message(new Channel::Message( |
| sizeof(BrokerMessageHeader) + expected_data_size, expected_num_handles)); |
| std::vector<base::ScopedFD> incoming_fds; |
| ssize_t read_result = |
| SocketRecvmsg(socket_fd, const_cast<void*>(message->data()), |
| message->data_num_bytes(), &incoming_fds, true /* block */); |
| bool error = false; |
| if (read_result < 0) { |
| PLOG(ERROR) << "Recvmsg error"; |
| error = true; |
| } else if (static_cast<size_t>(read_result) != message->data_num_bytes()) { |
| LOG(ERROR) << "Invalid node channel message"; |
| error = true; |
| } else if (incoming_fds.size() != expected_num_handles) { |
| LOG(ERROR) << "Received unexpected number of handles"; |
| error = true; |
| } |
| |
| if (error) |
| return nullptr; |
| |
| const BrokerMessageHeader* header = |
| reinterpret_cast<const BrokerMessageHeader*>(message->payload()); |
| if (header->type != expected_type) { |
| LOG(ERROR) << "Unexpected message"; |
| return nullptr; |
| } |
| |
| incoming_handles->reserve(incoming_fds.size()); |
| for (size_t i = 0; i < incoming_fds.size(); ++i) |
| incoming_handles->emplace_back(std::move(incoming_fds[i])); |
| |
| return message; |
| } |
| |
| } // namespace |
| |
| Broker::Broker(PlatformHandle handle) : sync_channel_(std::move(handle)) { |
| CHECK(sync_channel_.is_valid()); |
| |
| int fd = sync_channel_.GetFD().get(); |
| // Mark the channel as blocking. |
| int flags = fcntl(fd, F_GETFL); |
| PCHECK(flags != -1); |
| flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); |
| PCHECK(flags != -1); |
| |
| // Wait for the first message, which should contain a handle. |
| std::vector<PlatformHandle> incoming_platform_handles; |
| if (WaitForBrokerMessage(fd, BrokerMessageType::INIT, 1, 0, |
| &incoming_platform_handles)) { |
| inviter_endpoint_ = |
| PlatformChannelEndpoint(std::move(incoming_platform_handles[0])); |
| } |
| } |
| |
| Broker::~Broker() = default; |
| |
| PlatformChannelEndpoint Broker::GetInviterEndpoint() { |
| return std::move(inviter_endpoint_); |
| } |
| |
| base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion( |
| size_t num_bytes) { |
| base::AutoLock lock(lock_); |
| |
| BufferRequestData* buffer_request; |
| Channel::MessagePtr out_message = CreateBrokerMessage( |
| BrokerMessageType::BUFFER_REQUEST, 0, 0, &buffer_request); |
| buffer_request->size = num_bytes; |
| ssize_t write_result = |
| SocketWrite(sync_channel_.GetFD().get(), out_message->data(), |
| out_message->data_num_bytes()); |
| if (write_result < 0) { |
| PLOG(ERROR) << "Error sending sync broker message"; |
| return base::WritableSharedMemoryRegion(); |
| } else if (static_cast<size_t>(write_result) != |
| out_message->data_num_bytes()) { |
| LOG(ERROR) << "Error sending complete broker message"; |
| return base::WritableSharedMemoryRegion(); |
| } |
| |
| #if !defined(OS_POSIX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) || \ |
| (defined(OS_MACOSX) && !defined(OS_IOS)) |
| // Non-POSIX systems, as well as Android, Fuchsia, and non-iOS Mac, only use |
| // a single handle to represent a writable region. |
| constexpr size_t kNumExpectedHandles = 1; |
| #else |
| constexpr size_t kNumExpectedHandles = 2; |
| #endif |
| |
| std::vector<PlatformHandle> handles; |
| Channel::MessagePtr message = WaitForBrokerMessage( |
| sync_channel_.GetFD().get(), BrokerMessageType::BUFFER_RESPONSE, |
| kNumExpectedHandles, sizeof(BufferResponseData), &handles); |
| if (message) { |
| const BufferResponseData* data; |
| if (!GetBrokerMessageData(message.get(), &data)) |
| return base::WritableSharedMemoryRegion(); |
| |
| if (handles.size() == 1) |
| handles.emplace_back(); |
| return base::WritableSharedMemoryRegion::Deserialize( |
| base::subtle::PlatformSharedMemoryRegion::Take( |
| CreateSharedMemoryRegionHandleFromPlatformHandles( |
| std::move(handles[0]), std::move(handles[1])), |
| base::subtle::PlatformSharedMemoryRegion::Mode::kWritable, |
| num_bytes, |
| base::UnguessableToken::Deserialize(data->guid_high, |
| data->guid_low))); |
| } |
| |
| return base::WritableSharedMemoryRegion(); |
| } |
| |
| } // namespace core |
| } // namespace mojo |