blob: 8942ded4f9d7bf034ca2a8fe4e44424cf3feb938 [file] [log] [blame]
// Copyright 2024 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 <dirent.h>
#include <fcntl.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.hardware.pty/cpp/wire.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/fit/defer.h>
#include <lib/zx/channel.h>
#include <lib/zxio/null.h>
#include <lib/zxio/ops.h>
#include <lib/zxio/types.h>
#include <sys/stat.h>
#include <zircon/errors.h>
#include <zircon/syscalls.h>
#include <cstddef>
#include <cstdint>
#include "sdk/lib/zxio/private.h"
namespace fdevice = fuchsia_device;
namespace fio = fuchsia_io;
class Pty : public HasIo {
public:
Pty(fidl::ClientEnd<fuchsia_hardware_pty::Device> client_end, zx::eventpair event)
: HasIo(kOps), client_(std::move(client_end)), event_(std::move(event)) {}
void Destroy() { this->~Pty(); }
zx_status_t Close() {
if (client_.is_valid()) {
const fidl::WireResult result = client_->Close();
if (!result.ok()) {
return result.status();
}
const auto& response = result.value();
if (response.is_error()) {
return response.error_value();
}
}
return ZX_OK;
}
zx_status_t Release(zx_handle_t* out_handle) {
*out_handle = client_.TakeClientEnd().TakeChannel().release();
return ZX_OK;
}
zx_status_t Borrow(zx_handle_t* out_handle) {
*out_handle = client_.client_end().channel().get();
return ZX_OK;
}
zx_status_t Clone(zx_handle_t* out_handle) {
auto [client_end, server_end] = fidl::Endpoints<fuchsia_unknown::Cloneable>::Create();
const fidl::Status result = client_->Clone(std::move(server_end));
if (!result.ok()) {
return result.status();
}
*out_handle = client_end.TakeChannel().release();
return ZX_OK;
}
void WaitBegin(zxio_signals_t zxio_signals, zx_handle_t* out_handle,
zx_signals_t* out_zx_signals) {
*out_handle = event_.get();
zx_signals_t zx_signals = ZX_SIGNAL_NONE;
zx_signals |= [zxio_signals]() {
fdevice::wire::DeviceSignal signals;
if (zxio_signals & ZXIO_SIGNAL_READABLE) {
signals |= fdevice::wire::DeviceSignal::kReadable;
}
if (zxio_signals & ZXIO_SIGNAL_OUT_OF_BAND) {
signals |= fdevice::wire::DeviceSignal::kOob;
}
if (zxio_signals & ZXIO_SIGNAL_WRITABLE) {
signals |= fdevice::wire::DeviceSignal::kWritable;
}
if (zxio_signals & ZXIO_SIGNAL_ERROR) {
signals |= fdevice::wire::DeviceSignal::kError;
}
if (zxio_signals & ZXIO_SIGNAL_PEER_CLOSED) {
signals |= fdevice::wire::DeviceSignal::kHangup;
}
return static_cast<zx_signals_t>(signals);
}();
if (zxio_signals & ZXIO_SIGNAL_READ_DISABLED) {
zx_signals |= ZX_CHANNEL_PEER_CLOSED;
}
*out_zx_signals = zx_signals;
}
void WaitEnd(zx_signals_t zx_signals, zxio_signals_t* out_zxio_signals) {
zxio_signals_t zxio_signals = ZXIO_SIGNAL_NONE;
[&zxio_signals, signals = fdevice::wire::DeviceSignal::TruncatingUnknown(zx_signals)]() {
if (signals & fdevice::wire::DeviceSignal::kReadable) {
zxio_signals |= ZXIO_SIGNAL_READABLE;
}
if (signals & fdevice::wire::DeviceSignal::kOob) {
zxio_signals |= ZXIO_SIGNAL_OUT_OF_BAND;
}
if (signals & fdevice::wire::DeviceSignal::kWritable) {
zxio_signals |= ZXIO_SIGNAL_WRITABLE;
}
if (signals & fdevice::wire::DeviceSignal::kError) {
zxio_signals |= ZXIO_SIGNAL_ERROR;
}
if (signals & fdevice::wire::DeviceSignal::kHangup) {
zxio_signals |= ZXIO_SIGNAL_PEER_CLOSED;
}
}();
if (zx_signals & ZX_CHANNEL_PEER_CLOSED) {
zxio_signals |= ZXIO_SIGNAL_READ_DISABLED;
}
*out_zxio_signals = zxio_signals;
}
zx_status_t Readv(const zx_iovec_t* vector, size_t vector_count, zxio_flags_t flags,
size_t* out_actual) {
// fuchsia.hardware.pty/Device composes fuchsia.io/Readable.
return RemoteReadv(fidl::UnownedClientEnd<fio::Readable>(client_.client_end().handle()), vector,
vector_count, flags, out_actual);
}
zx_status_t Writev(const zx_iovec_t* vector, size_t vector_count, zxio_flags_t flags,
size_t* out_actual) {
// fuchsia.hardware.pty/Device composes fuchsia.io/Writable.
return RemoteWritev(fidl::UnownedClientEnd<fio::Writable>(client_.client_end().handle()),
vector, vector_count, flags, out_actual);
}
zx_status_t IsAtty(bool* tty) {
*tty = true;
return ZX_OK;
}
zx_status_t GetWindowSize(uint32_t* width, uint32_t* height) {
if (!client_.is_valid()) {
return ZX_ERR_BAD_STATE;
}
const fidl::WireResult result = client_->GetWindowSize();
if (!result.ok()) {
return ZX_ERR_NOT_SUPPORTED;
}
const auto& response = result.value();
if (response.status != ZX_OK) {
return ZX_ERR_NOT_SUPPORTED;
}
*width = response.size.width;
*height = response.size.height;
return ZX_OK;
}
zx_status_t SetWindowSize(uint32_t width, uint32_t height) {
if (!client_.is_valid()) {
return ZX_ERR_BAD_STATE;
}
const fuchsia_hardware_pty::wire::WindowSize size = {
.width = width,
.height = height,
};
const fidl::WireResult result = client_->SetWindowSize(size);
if (!result.ok()) {
return ZX_ERR_NOT_SUPPORTED;
}
const auto& response = result.value();
if (response.status != ZX_OK) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
private:
static const zxio_ops_t kOps;
fidl::WireSyncClient<fuchsia_hardware_pty::Device> client_;
const zx::eventpair event_;
};
constexpr zxio_ops_t Pty::kOps = ([]() {
using Adaptor = Adaptor<Pty>;
zxio_ops_t ops = zxio_default_ops;
ops.destroy = Adaptor::From<&Pty::Destroy>;
ops.close = Adaptor::From<&Pty::Close>;
ops.release = Adaptor::From<&Pty::Release>;
ops.borrow = Adaptor::From<&Pty::Borrow>;
ops.clone = Adaptor::From<&Pty::Clone>;
ops.wait_begin = Adaptor::From<&Pty::WaitBegin>;
ops.wait_end = Adaptor::From<&Pty::WaitEnd>;
ops.readv = Adaptor::From<&Pty::Readv>;
ops.writev = Adaptor::From<&Pty::Writev>;
ops.isatty = Adaptor::From<&Pty::IsAtty>;
ops.get_window_size = Adaptor::From<&Pty::GetWindowSize>;
ops.set_window_size = Adaptor::From<&Pty::SetWindowSize>;
return ops;
})();
zx_status_t zxio_pty_init(zxio_storage_t* storage, zx::eventpair event,
fidl::ClientEnd<fuchsia_hardware_pty::Device> client) {
new (storage) Pty(std::move(client), std::move(event));
return ZX_OK;
}