/* * Copyright (c) 2024, Andrew Kaster * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #if defined(AK_OS_GNU_HURD) extern "C" { # include } // This is in on Darwin, and doesn't seem to be required. #endif #if defined(AK_OS_MACOS) # include #endif namespace Core { static constexpr MachPort::PortRight associated_port_right(MachPort::MessageRight right) { switch (right) { case MachPort::MessageRight::MoveReceive: return MachPort::PortRight::Receive; case MachPort::MessageRight::MoveSend: case MachPort::MessageRight::CopySend: case MachPort::MessageRight::MakeSend: return MachPort::PortRight::Send; case MachPort::MessageRight::MoveSendOnce: case MachPort::MessageRight::MakeSendOnce: return MachPort::PortRight::SendOnce; #if defined(AK_OS_MACOS) case MachPort::MessageRight::CopyReceive: case MachPort::MessageRight::DisposeReceive: return MachPort::PortRight::Receive; case MachPort::MessageRight::DisposeSend: return MachPort::PortRight::Send; case MachPort::MessageRight::DisposeSendOnce: return MachPort::PortRight::SendOnce; #endif } VERIFY_NOT_REACHED(); } Error mach_error_to_error(kern_return_t error) { char const* err_string = mach_error_string(error); StringView const err_view(err_string, strlen(err_string)); return Error::from_string_view(err_view); } #if defined(AK_OS_MACOS) static Error bootstrap_error_to_error(kern_return_t error) { char const* err_string = bootstrap_strerror(error); StringView const err_view(err_string, strlen(err_string)); return Error::from_string_view(err_view); } #endif MachPort::MachPort(PortRight right, mach_port_t port) : m_right(right) , m_port(port) { } MachPort::~MachPort() { unref_port(); } MachPort::MachPort(MachPort&& other) : m_right(other.m_right) , m_port(other.release()) { } MachPort& MachPort::operator=(MachPort&& other) { if (this != &other) { unref_port(); m_right = other.m_right; m_port = other.release(); } return *this; } void MachPort::unref_port() { if (!MACH_PORT_VALID(m_port)) return; kern_return_t res = KERN_FAILURE; switch (m_right) { case PortRight::Send: case PortRight::SendOnce: case PortRight::DeadName: res = mach_port_deallocate(mach_task_self(), m_port); break; case PortRight::Receive: case PortRight::PortSet: res = mach_port_mod_refs(mach_task_self(), m_port, to_underlying(m_right), -1); break; } VERIFY(res == KERN_SUCCESS); } ErrorOr MachPort::create_with_right(PortRight right) { mach_port_t port = MACH_PORT_NULL; auto const ret = mach_port_allocate(mach_task_self(), to_underlying(right), &port); if (ret != KERN_SUCCESS) { dbgln("Unable to allocate port with right: {}", to_underlying(right)); return mach_error_to_error(ret); } return MachPort(right, port); } MachPort MachPort::adopt_right(mach_port_t port, PortRight right) { return MachPort(right, port); } mach_port_t MachPort::release() { return exchange(m_port, MACH_PORT_NULL); } ErrorOr MachPort::insert_right(MessageRight right) { auto const ret = mach_port_insert_right(mach_task_self(), m_port, m_port, to_underlying(right)); if (ret != KERN_SUCCESS) { dbgln("Unable to insert message right: {}", to_underlying(right)); return mach_error_to_error(ret); } return MachPort(associated_port_right(right), m_port); } #if defined(AK_OS_MACOS) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" // bootstrap_register has been deprecated since macOS 10.5, but rules are more 'guidelines' than actual rules ErrorOr MachPort::register_with_bootstrap_server(ByteString const& service_name) { if (service_name.length() > sizeof(name_t) - 1) return Error::from_errno(E2BIG); auto const ret = bootstrap_register(bootstrap_port, const_cast(service_name.characters()), m_port); if (ret != KERN_SUCCESS) { dbgln("Unable to register {} with bootstrap on port {:p}", service_name, m_port); return bootstrap_error_to_error(ret); } return {}; } # pragma GCC diagnostic pop ErrorOr MachPort::look_up_from_bootstrap_server(ByteString const& service_name) { if (service_name.length() > sizeof(name_t) - 1) return Error::from_errno(E2BIG); mach_port_t port = MACH_PORT_NULL; auto const ret = bootstrap_look_up(bootstrap_port, service_name.characters(), &port); if (ret != KERN_SUCCESS) { dbgln("Unable to look up service {} in bootstrap", service_name); return bootstrap_error_to_error(ret); } return MachPort(PortRight::Send, port); } #endif }