| // Copyright 2021 The Fuchsia 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 <lib/fidl/internal.h> |
| #include <lib/fidl/llcpp/internal/debug_thread_checker.h> |
| #include <lib/fidl/llcpp/internal/transport_channel.h> |
| #include <lib/fidl/llcpp/message.h> |
| #include <lib/fidl/llcpp/message_storage.h> |
| #include <lib/fidl/trace.h> |
| #include <zircon/syscalls.h> |
| |
| #include <cstring> |
| |
| namespace fidl { |
| namespace internal { |
| |
| namespace { |
| |
| zx_status_t channel_write(fidl_handle_t handle, WriteOptions write_options, const void* data, |
| uint32_t data_count, const fidl_handle_t* handles, |
| const void* handle_metadata, uint32_t handles_count) { |
| zx_handle_disposition_t hds[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| const fidl_channel_handle_metadata_t* metadata = |
| static_cast<const fidl_channel_handle_metadata_t*>(handle_metadata); |
| for (uint32_t i = 0; i < handles_count; i++) { |
| hds[i] = zx_handle_disposition_t{ |
| .operation = ZX_HANDLE_OP_MOVE, |
| .handle = handles[i], |
| .type = metadata[i].obj_type, |
| .rights = metadata[i].rights, |
| .result = ZX_OK, |
| }; |
| } |
| return zx_channel_write_etc(handle, ZX_CHANNEL_WRITE_USE_IOVEC, data, data_count, |
| reinterpret_cast<zx_handle_disposition_t*>(hds), handles_count); |
| } |
| |
| zx_status_t channel_read(fidl_handle_t handle, const ReadOptions& read_options, void* data, |
| uint32_t data_capacity, fidl_handle_t* handles, void* handle_metadata, |
| uint32_t handles_capacity, uint32_t* out_data_actual_count, |
| uint32_t* out_handles_actual_count) { |
| uint32_t options = 0; |
| if (read_options.discardable) { |
| options |= ZX_CHANNEL_READ_MAY_DISCARD; |
| } |
| |
| *out_data_actual_count = 0; |
| *out_handles_actual_count = 0; |
| zx_handle_info_t his[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| zx_status_t status = |
| zx_channel_read_etc(handle, options, data, his, data_capacity, handles_capacity, |
| out_data_actual_count, out_handles_actual_count); |
| fidl_channel_handle_metadata_t* metadata = |
| static_cast<fidl_channel_handle_metadata_t*>(handle_metadata); |
| for (uint32_t i = 0; i < *out_handles_actual_count; i++) { |
| handles[i] = his[i].handle; |
| metadata[i] = fidl_channel_handle_metadata_t{ |
| .obj_type = his[i].type, |
| .rights = his[i].rights, |
| }; |
| } |
| return status; |
| } |
| |
| zx_status_t channel_call(fidl_handle_t handle, CallOptions call_options, |
| const CallMethodArgs& cargs, uint32_t* out_data_actual_count, |
| uint32_t* out_handles_actual_count) { |
| ZX_DEBUG_ASSERT(cargs.out_rd_data == nullptr); |
| ZX_DEBUG_ASSERT(cargs.rd_data != nullptr); |
| |
| zx_handle_disposition_t hds[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| const fidl_channel_handle_metadata_t* wr_metadata = |
| reinterpret_cast<const fidl_channel_handle_metadata_t*>(cargs.wr_handle_metadata); |
| for (uint32_t i = 0; i < cargs.wr_handles_count; i++) { |
| hds[i] = zx_handle_disposition_t{ |
| .operation = ZX_HANDLE_OP_MOVE, |
| .handle = cargs.wr_handles[i], |
| .type = wr_metadata[i].obj_type, |
| .rights = wr_metadata[i].rights, |
| .result = ZX_OK, |
| }; |
| } |
| zx_handle_info_t his[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| zx_channel_call_etc_args_t args = { |
| .wr_bytes = cargs.wr_data, |
| .wr_handles = hds, |
| .rd_bytes = cargs.rd_data, |
| .rd_handles = his, |
| .wr_num_bytes = cargs.wr_data_count, |
| .wr_num_handles = cargs.wr_handles_count, |
| .rd_num_bytes = cargs.rd_data_capacity, |
| .rd_num_handles = cargs.rd_handles_capacity, |
| }; |
| zx_status_t status = |
| zx_channel_call_etc(handle, ZX_CHANNEL_WRITE_USE_IOVEC, call_options.deadline, &args, |
| out_data_actual_count, out_handles_actual_count); |
| fidl_channel_handle_metadata_t* rd_metadata = |
| reinterpret_cast<fidl_channel_handle_metadata_t*>(cargs.rd_handle_metadata); |
| for (uint32_t i = 0; i < *out_handles_actual_count; i++) { |
| cargs.rd_handles[i] = his[i].handle; |
| rd_metadata[i] = fidl_channel_handle_metadata_t{ |
| .obj_type = his[i].type, |
| .rights = his[i].rights, |
| }; |
| } |
| return status; |
| } |
| |
| zx_status_t channel_create_waiter(fidl_handle_t handle, async_dispatcher_t* dispatcher, |
| TransportWaitSuccessHandler success_handler, |
| TransportWaitFailureHandler failure_handler, |
| AnyTransportWaiter& any_transport_waiter) { |
| any_transport_waiter.emplace<ChannelWaiter>(handle, dispatcher, std::move(success_handler), |
| std::move(failure_handler)); |
| return ZX_OK; |
| } |
| |
| void channel_create_thread_checker(async_dispatcher_t* dispatcher, ThreadingPolicy threading_policy, |
| AnyThreadChecker& any_thread_checker) { |
| any_thread_checker.emplace<ZirconThreadChecker>(threading_policy); |
| } |
| |
| void channel_close(fidl_handle_t handle) { zx_handle_close(handle); } |
| void channel_close_many(const fidl_handle_t* handles, size_t num_handles) { |
| zx_handle_close_many(handles, num_handles); |
| } |
| |
| } // namespace |
| |
| const TransportVTable ChannelTransport::VTable = { |
| .type = FIDL_TRANSPORT_TYPE_CHANNEL, |
| .encoding_configuration = &ChannelTransport::EncodingConfiguration, |
| .write = channel_write, |
| .read = channel_read, |
| .call = channel_call, |
| .create_waiter = channel_create_waiter, |
| .create_thread_checker = channel_create_thread_checker, |
| }; |
| |
| void ChannelWaiter::HandleWaitFinished(async_dispatcher_t* dispatcher, zx_status_t status, |
| const zx_packet_signal_t* signal) { |
| if (status != ZX_OK) { |
| return failure_handler_(fidl::UnbindInfo::DispatcherError(status)); |
| } |
| if (!(signal->observed & ZX_CHANNEL_READABLE)) { |
| ZX_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED); |
| return failure_handler_(fidl::UnbindInfo::PeerClosed(ZX_ERR_PEER_CLOSED)); |
| } |
| |
| FIDL_INTERNAL_DISABLE_AUTO_VAR_INIT InlineMessageBuffer<ZX_CHANNEL_MAX_MSG_BYTES> bytes; |
| FIDL_INTERNAL_DISABLE_AUTO_VAR_INIT zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| FIDL_INTERNAL_DISABLE_AUTO_VAR_INIT fidl_channel_handle_metadata_t |
| handle_metadata[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| fidl_trace(WillLLCPPAsyncChannelRead); |
| IncomingMessage msg = fidl::MessageRead(zx::unowned_channel(async_wait_t::object), bytes.view(), |
| handles, handle_metadata, ZX_CHANNEL_MAX_MSG_HANDLES); |
| if (!msg.ok()) { |
| return failure_handler_(fidl::UnbindInfo{msg}); |
| } |
| fidl_trace(DidLLCPPAsyncChannelRead, nullptr /* type */, bytes.data(), msg.byte_actual(), |
| msg.handle_actual()); |
| return success_handler_(msg, IncomingTransportContext()); |
| } |
| |
| namespace { |
| |
| zx_status_t channel_encode_process_handle(HandleAttributes attr, uint32_t metadata_index, |
| void* out_metadata_array, const char** out_error) { |
| reinterpret_cast<fidl_channel_handle_metadata_t*>(out_metadata_array)[metadata_index] = { |
| .obj_type = attr.obj_type, .rights = attr.rights}; |
| return ZX_OK; |
| } |
| zx_status_t channel_decode_process_handle(fidl_handle_t* handle, HandleAttributes attr, |
| uint32_t metadata_index, const void* metadata_array, |
| const char** error) { |
| fidl_channel_handle_metadata_t v = |
| reinterpret_cast<const fidl_channel_handle_metadata_t*>(metadata_array)[metadata_index]; |
| return FidlEnsureHandleRights(handle, v.obj_type, v.rights, attr.obj_type, attr.rights, error); |
| } |
| |
| } // namespace |
| |
| const CodingConfig ChannelTransport::EncodingConfiguration = { |
| .max_iovecs_write = ZX_CHANNEL_MAX_MSG_IOVECS, |
| .handle_metadata_stride = sizeof(fidl_channel_handle_metadata_t), |
| .encode_process_handle = channel_encode_process_handle, |
| .decode_process_handle = channel_decode_process_handle, |
| .close = channel_close, |
| .close_many = channel_close_many, |
| }; |
| |
| } // namespace internal |
| } // namespace fidl |