mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-09 09:34:57 +09:00
Everywhere: Hoist the Libraries folder to the top-level
This commit is contained in:
parent
950e819ee7
commit
93712b24bf
Notes:
github-actions[bot]
2024-11-10 11:51:52 +00:00
Author: https://github.com/trflynn89
Commit: 93712b24bf
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2256
Reviewed-by: https://github.com/sideshowbarker
4547 changed files with 104 additions and 113 deletions
13
Libraries/LibIPC/CMakeLists.txt
Normal file
13
Libraries/LibIPC/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
set(SOURCES
|
||||
Connection.cpp
|
||||
Decoder.cpp
|
||||
Encoder.cpp
|
||||
Message.cpp
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
list(APPEND SOURCES TransportSocket.cpp)
|
||||
endif()
|
||||
|
||||
serenity_lib(LibIPC ipc)
|
||||
target_link_libraries(LibIPC PRIVATE LibCore LibURL LibThreading)
|
80
Libraries/LibIPC/Concepts.h
Normal file
80
Libraries/LibIPC/Concepts.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/SharedCircularQueue.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
// These concepts are used to help the compiler distinguish between specializations that would be
|
||||
// ambiguous otherwise. For example, if the specializations for int and Vector<T> were declared as
|
||||
// follows:
|
||||
//
|
||||
// template<> ErrorOr<int> decode(Decoder& decoder);
|
||||
// template<typename T> ErrorOr<Vector<T>> decode(Decoder& decoder);
|
||||
//
|
||||
// Then decode<int>() would be ambiguous because either declaration could work (the compiler would
|
||||
// not be able to distinguish if you wanted to decode an int or a Vector of int).
|
||||
//
|
||||
// They also serve to work around the inability to do partial function specialization in C++.
|
||||
namespace IPC::Concepts {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsHashMap = false;
|
||||
template<typename K, typename V, typename KeyTraits, typename ValueTraits, bool IsOrdered>
|
||||
constexpr inline bool IsHashMap<HashMap<K, V, KeyTraits, ValueTraits, IsOrdered>> = true;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsOptional = false;
|
||||
template<typename T>
|
||||
constexpr inline bool IsOptional<Optional<T>> = true;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsSharedSingleProducerCircularQueue = false;
|
||||
template<typename T, size_t Size>
|
||||
constexpr inline bool IsSharedSingleProducerCircularQueue<Core::SharedSingleProducerCircularQueue<T, Size>> = true;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsVariant = false;
|
||||
template<typename... Ts>
|
||||
constexpr inline bool IsVariant<Variant<Ts...>> = true;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsVector = false;
|
||||
template<typename T>
|
||||
constexpr inline bool IsVector<Vector<T>> = true;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsArray = false;
|
||||
template<typename T, size_t N>
|
||||
constexpr inline bool IsArray<Array<T, N>> = true;
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept HashMap = Detail::IsHashMap<T>;
|
||||
|
||||
template<typename T>
|
||||
concept Optional = Detail::IsOptional<T>;
|
||||
|
||||
template<typename T>
|
||||
concept SharedSingleProducerCircularQueue = Detail::IsSharedSingleProducerCircularQueue<T>;
|
||||
|
||||
template<typename T>
|
||||
concept Variant = Detail::IsVariant<T>;
|
||||
|
||||
template<typename T>
|
||||
concept Vector = Detail::IsVector<T>;
|
||||
|
||||
template<typename T>
|
||||
concept Array = Detail::IsArray<T>;
|
||||
|
||||
}
|
234
Libraries/LibIPC/Connection.cpp
Normal file
234
Libraries/LibIPC/Connection.cpp
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2024, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibIPC/Connection.h>
|
||||
#include <LibIPC/Message.h>
|
||||
#include <LibIPC/Stub.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
ConnectionBase::ConnectionBase(IPC::Stub& local_stub, Transport transport, u32 local_endpoint_magic)
|
||||
: m_local_stub(local_stub)
|
||||
, m_transport(move(transport))
|
||||
, m_local_endpoint_magic(local_endpoint_magic)
|
||||
{
|
||||
m_responsiveness_timer = Core::Timer::create_single_shot(3000, [this] { may_have_become_unresponsive(); });
|
||||
|
||||
m_transport.set_up_read_hook([this] {
|
||||
NonnullRefPtr protect = *this;
|
||||
// FIXME: Do something about errors.
|
||||
(void)drain_messages_from_peer();
|
||||
handle_messages();
|
||||
});
|
||||
|
||||
m_send_queue = adopt_ref(*new SendQueue);
|
||||
m_send_thread = Threading::Thread::construct([this, queue = m_send_queue]() -> intptr_t {
|
||||
for (;;) {
|
||||
queue->mutex.lock();
|
||||
while (queue->messages.is_empty() && queue->running)
|
||||
queue->condition.wait();
|
||||
|
||||
if (!queue->running) {
|
||||
queue->mutex.unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
auto message = queue->messages.take_first();
|
||||
queue->mutex.unlock();
|
||||
|
||||
if (auto result = message.transfer_message(m_transport); result.is_error()) {
|
||||
dbgln("ConnectionBase::send_thread: {}", result.error());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
m_send_thread->start();
|
||||
}
|
||||
|
||||
ConnectionBase::~ConnectionBase()
|
||||
{
|
||||
{
|
||||
Threading::MutexLocker locker(m_send_queue->mutex);
|
||||
m_send_queue->running = false;
|
||||
m_send_queue->condition.signal();
|
||||
}
|
||||
m_send_thread->detach();
|
||||
}
|
||||
|
||||
bool ConnectionBase::is_open() const
|
||||
{
|
||||
return m_transport.is_open();
|
||||
}
|
||||
|
||||
ErrorOr<void> ConnectionBase::post_message(Message const& message)
|
||||
{
|
||||
return post_message(TRY(message.encode()));
|
||||
}
|
||||
|
||||
ErrorOr<void> ConnectionBase::post_message(MessageBuffer buffer)
|
||||
{
|
||||
// NOTE: If this connection is being shut down, but has not yet been destroyed,
|
||||
// the socket will be closed. Don't try to send more messages.
|
||||
if (!m_transport.is_open())
|
||||
return Error::from_string_literal("Trying to post_message during IPC shutdown");
|
||||
|
||||
{
|
||||
Threading::MutexLocker locker(m_send_queue->mutex);
|
||||
m_send_queue->messages.append(move(buffer));
|
||||
m_send_queue->condition.signal();
|
||||
}
|
||||
|
||||
m_responsiveness_timer->start();
|
||||
return {};
|
||||
}
|
||||
|
||||
void ConnectionBase::shutdown()
|
||||
{
|
||||
m_transport.close();
|
||||
die();
|
||||
}
|
||||
|
||||
void ConnectionBase::shutdown_with_error(Error const& error)
|
||||
{
|
||||
dbgln("IPC::ConnectionBase ({:p}) had an error ({}), disconnecting.", this, error);
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void ConnectionBase::handle_messages()
|
||||
{
|
||||
auto messages = move(m_unprocessed_messages);
|
||||
for (auto& message : messages) {
|
||||
if (message->endpoint_magic() == m_local_endpoint_magic) {
|
||||
auto handler_result = m_local_stub.handle(*message);
|
||||
if (handler_result.is_error()) {
|
||||
dbgln("IPC::ConnectionBase::handle_messages: {}", handler_result.error());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto response = handler_result.release_value()) {
|
||||
if (auto post_result = post_message(*response); post_result.is_error()) {
|
||||
dbgln("IPC::ConnectionBase::handle_messages: {}", post_result.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionBase::wait_for_transport_to_become_readable()
|
||||
{
|
||||
m_transport.wait_until_readable();
|
||||
}
|
||||
|
||||
ErrorOr<Vector<u8>> ConnectionBase::read_as_much_as_possible_from_transport_without_blocking()
|
||||
{
|
||||
Vector<u8> bytes;
|
||||
|
||||
if (!m_unprocessed_bytes.is_empty()) {
|
||||
bytes.append(m_unprocessed_bytes.data(), m_unprocessed_bytes.size());
|
||||
m_unprocessed_bytes.clear();
|
||||
}
|
||||
|
||||
bool should_shut_down = false;
|
||||
auto schedule_shutdown = [this, &should_shut_down]() {
|
||||
should_shut_down = true;
|
||||
deferred_invoke([this] {
|
||||
shutdown();
|
||||
});
|
||||
};
|
||||
|
||||
auto&& [new_bytes, received_fds] = m_transport.read_as_much_as_possible_without_blocking(move(schedule_shutdown));
|
||||
bytes.append(new_bytes.data(), new_bytes.size());
|
||||
|
||||
for (auto const& fd : received_fds)
|
||||
m_unprocessed_fds.enqueue(IPC::File::adopt_fd(fd));
|
||||
|
||||
if (!bytes.is_empty()) {
|
||||
m_responsiveness_timer->stop();
|
||||
did_become_responsive();
|
||||
} else if (should_shut_down) {
|
||||
return Error::from_string_literal("IPC connection EOF");
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
ErrorOr<void> ConnectionBase::drain_messages_from_peer()
|
||||
{
|
||||
auto bytes = TRY(read_as_much_as_possible_from_transport_without_blocking());
|
||||
|
||||
size_t index = 0;
|
||||
try_parse_messages(bytes, index);
|
||||
|
||||
if (index < bytes.size()) {
|
||||
// Sometimes we might receive a partial message. That's okay, just stash away
|
||||
// the unprocessed bytes and we'll prepend them to the next incoming message
|
||||
// in the next run of this function.
|
||||
auto remaining_bytes = TRY(ByteBuffer::copy(bytes.span().slice(index)));
|
||||
if (!m_unprocessed_bytes.is_empty()) {
|
||||
shutdown();
|
||||
return Error::from_string_literal("drain_messages_from_peer: Already have unprocessed bytes");
|
||||
}
|
||||
m_unprocessed_bytes = move(remaining_bytes);
|
||||
}
|
||||
|
||||
if (!m_unprocessed_messages.is_empty()) {
|
||||
deferred_invoke([this] {
|
||||
handle_messages();
|
||||
});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
OwnPtr<IPC::Message> ConnectionBase::wait_for_specific_endpoint_message_impl(u32 endpoint_magic, int message_id)
|
||||
{
|
||||
for (;;) {
|
||||
// Double check we don't already have the event waiting for us.
|
||||
// Otherwise we might end up blocked for a while for no reason.
|
||||
for (size_t i = 0; i < m_unprocessed_messages.size(); ++i) {
|
||||
auto& message = m_unprocessed_messages[i];
|
||||
if (message->endpoint_magic() != endpoint_magic)
|
||||
continue;
|
||||
if (message->message_id() == message_id)
|
||||
return m_unprocessed_messages.take(i);
|
||||
}
|
||||
|
||||
if (!is_open())
|
||||
break;
|
||||
|
||||
wait_for_transport_to_become_readable();
|
||||
if (drain_messages_from_peer().is_error())
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ConnectionBase::try_parse_messages(Vector<u8> const& bytes, size_t& index)
|
||||
{
|
||||
u32 message_size = 0;
|
||||
for (; index + sizeof(message_size) < bytes.size(); index += message_size) {
|
||||
memcpy(&message_size, bytes.data() + index, sizeof(message_size));
|
||||
if (message_size == 0 || bytes.size() - index - sizeof(uint32_t) < message_size)
|
||||
break;
|
||||
index += sizeof(message_size);
|
||||
auto remaining_bytes = ReadonlyBytes { bytes.data() + index, message_size };
|
||||
|
||||
if (auto message = try_parse_message(remaining_bytes, m_unprocessed_fds)) {
|
||||
m_unprocessed_messages.append(message.release_nonnull());
|
||||
continue;
|
||||
}
|
||||
|
||||
dbgln("Failed to parse IPC message:");
|
||||
dbgln("{:hex-dump}", remaining_bytes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
130
Libraries/LibIPC/Connection.h
Normal file
130
Libraries/LibIPC/Connection.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Queue.h>
|
||||
#include <LibCore/EventReceiver.h>
|
||||
#include <LibIPC/File.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
#include <LibIPC/Transport.h>
|
||||
#include <LibThreading/ConditionVariable.h>
|
||||
#include <LibThreading/MutexProtected.h>
|
||||
#include <LibThreading/Thread.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class ConnectionBase : public Core::EventReceiver {
|
||||
C_OBJECT_ABSTRACT(ConnectionBase);
|
||||
|
||||
public:
|
||||
virtual ~ConnectionBase() override;
|
||||
|
||||
[[nodiscard]] bool is_open() const;
|
||||
ErrorOr<void> post_message(Message const&);
|
||||
|
||||
void shutdown();
|
||||
virtual void die() { }
|
||||
|
||||
Transport& transport() { return m_transport; }
|
||||
|
||||
protected:
|
||||
explicit ConnectionBase(IPC::Stub&, Transport, u32 local_endpoint_magic);
|
||||
|
||||
virtual void may_have_become_unresponsive() { }
|
||||
virtual void did_become_responsive() { }
|
||||
virtual void shutdown_with_error(Error const&);
|
||||
virtual OwnPtr<Message> try_parse_message(ReadonlyBytes, Queue<IPC::File>&) = 0;
|
||||
|
||||
OwnPtr<IPC::Message> wait_for_specific_endpoint_message_impl(u32 endpoint_magic, int message_id);
|
||||
void wait_for_transport_to_become_readable();
|
||||
ErrorOr<Vector<u8>> read_as_much_as_possible_from_transport_without_blocking();
|
||||
ErrorOr<void> drain_messages_from_peer();
|
||||
void try_parse_messages(Vector<u8> const& bytes, size_t& index);
|
||||
|
||||
ErrorOr<void> post_message(MessageBuffer);
|
||||
void handle_messages();
|
||||
|
||||
IPC::Stub& m_local_stub;
|
||||
|
||||
Transport m_transport;
|
||||
|
||||
RefPtr<Core::Timer> m_responsiveness_timer;
|
||||
|
||||
Vector<NonnullOwnPtr<Message>> m_unprocessed_messages;
|
||||
Queue<IPC::File> m_unprocessed_fds;
|
||||
ByteBuffer m_unprocessed_bytes;
|
||||
|
||||
u32 m_local_endpoint_magic { 0 };
|
||||
|
||||
struct SendQueue : public AtomicRefCounted<SendQueue> {
|
||||
AK::SinglyLinkedList<MessageBuffer> messages;
|
||||
Threading::Mutex mutex;
|
||||
Threading::ConditionVariable condition { mutex };
|
||||
bool running { true };
|
||||
};
|
||||
|
||||
RefPtr<Threading::Thread> m_send_thread;
|
||||
RefPtr<SendQueue> m_send_queue;
|
||||
};
|
||||
|
||||
template<typename LocalEndpoint, typename PeerEndpoint>
|
||||
class Connection : public ConnectionBase {
|
||||
public:
|
||||
Connection(IPC::Stub& local_stub, Transport transport)
|
||||
: ConnectionBase(local_stub, move(transport), LocalEndpoint::static_magic())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename MessageType>
|
||||
OwnPtr<MessageType> wait_for_specific_message()
|
||||
{
|
||||
return wait_for_specific_endpoint_message<MessageType, LocalEndpoint>();
|
||||
}
|
||||
|
||||
template<typename RequestType, typename... Args>
|
||||
NonnullOwnPtr<typename RequestType::ResponseType> send_sync(Args&&... args)
|
||||
{
|
||||
MUST(post_message(RequestType(forward<Args>(args)...)));
|
||||
auto response = wait_for_specific_endpoint_message<typename RequestType::ResponseType, PeerEndpoint>();
|
||||
VERIFY(response);
|
||||
return response.release_nonnull();
|
||||
}
|
||||
|
||||
template<typename RequestType, typename... Args>
|
||||
OwnPtr<typename RequestType::ResponseType> send_sync_but_allow_failure(Args&&... args)
|
||||
{
|
||||
if (post_message(RequestType(forward<Args>(args)...)).is_error())
|
||||
return nullptr;
|
||||
return wait_for_specific_endpoint_message<typename RequestType::ResponseType, PeerEndpoint>();
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename MessageType, typename Endpoint>
|
||||
OwnPtr<MessageType> wait_for_specific_endpoint_message()
|
||||
{
|
||||
if (auto message = wait_for_specific_endpoint_message_impl(Endpoint::static_magic(), MessageType::static_message_id()))
|
||||
return message.template release_nonnull<MessageType>();
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual OwnPtr<Message> try_parse_message(ReadonlyBytes bytes, Queue<IPC::File>& fds) override
|
||||
{
|
||||
auto local_message = LocalEndpoint::decode_message(bytes, fds);
|
||||
if (!local_message.is_error())
|
||||
return local_message.release_value();
|
||||
|
||||
auto peer_message = PeerEndpoint::decode_message(bytes, fds);
|
||||
if (!peer_message.is_error())
|
||||
return peer_message.release_value();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
73
Libraries/LibIPC/ConnectionFromClient.h
Normal file
73
Libraries/LibIPC/ConnectionFromClient.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibIPC/Connection.h>
|
||||
#include <LibIPC/Stub.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<typename T, class... Args>
|
||||
NonnullRefPtr<T> new_client_connection(Args&&... args)
|
||||
{
|
||||
return T::construct(forward<Args>(args)...) /* arghs */;
|
||||
}
|
||||
|
||||
template<typename ClientEndpoint, typename ServerEndpoint>
|
||||
class ConnectionFromClient : public Connection<ServerEndpoint, ClientEndpoint>
|
||||
, public ServerEndpoint::Stub
|
||||
, public ClientEndpoint::template Proxy<ServerEndpoint> {
|
||||
public:
|
||||
using ServerStub = typename ServerEndpoint::Stub;
|
||||
using IPCProxy = typename ClientEndpoint::template Proxy<ServerEndpoint>;
|
||||
|
||||
ConnectionFromClient(ServerStub& stub, Transport transport, int client_id)
|
||||
: IPC::Connection<ServerEndpoint, ClientEndpoint>(stub, move(transport))
|
||||
, ClientEndpoint::template Proxy<ServerEndpoint>(*this, {})
|
||||
, m_client_id(client_id)
|
||||
{
|
||||
this->transport().set_up_read_hook([this] {
|
||||
NonnullRefPtr protect = *this;
|
||||
// FIXME: Do something about errors.
|
||||
(void)this->drain_messages_from_peer();
|
||||
});
|
||||
}
|
||||
|
||||
virtual ~ConnectionFromClient() override = default;
|
||||
|
||||
void did_misbehave()
|
||||
{
|
||||
dbgln("{} (id={}) misbehaved, disconnecting.", *this, m_client_id);
|
||||
this->shutdown();
|
||||
}
|
||||
|
||||
void did_misbehave(char const* message)
|
||||
{
|
||||
dbgln("{} (id={}) misbehaved ({}), disconnecting.", *this, m_client_id, message);
|
||||
this->shutdown();
|
||||
}
|
||||
|
||||
virtual void shutdown_with_error(Error const& error) override
|
||||
{
|
||||
dbgln("{} (id={}) had an error ({}), disconnecting.", *this, m_client_id, error);
|
||||
this->shutdown();
|
||||
}
|
||||
|
||||
int client_id() const { return m_client_id; }
|
||||
|
||||
virtual void die() override = 0;
|
||||
|
||||
private:
|
||||
int m_client_id { -1 };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename ClientEndpoint, typename ServerEndpoint>
|
||||
struct AK::Formatter<IPC::ConnectionFromClient<ClientEndpoint, ServerEndpoint>> : Formatter<Core::EventReceiver> {
|
||||
};
|
49
Libraries/LibIPC/ConnectionToServer.h
Normal file
49
Libraries/LibIPC/ConnectionToServer.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCore/SessionManagement.h>
|
||||
#include <LibIPC/Connection.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
#define IPC_CLIENT_CONNECTION(klass, socket_path) \
|
||||
C_OBJECT_ABSTRACT(klass) \
|
||||
public: \
|
||||
template<typename Klass = klass, class... Args> \
|
||||
static ErrorOr<NonnullRefPtr<klass>> try_create(Args&&... args) \
|
||||
{ \
|
||||
auto parsed_socket_path = TRY(Core::SessionManagement::parse_path_with_sid(socket_path)); \
|
||||
auto socket = TRY(Core::LocalSocket::connect(move(parsed_socket_path))); \
|
||||
/* We want to rate-limit our clients */ \
|
||||
TRY(socket->set_blocking(true)); \
|
||||
\
|
||||
return adopt_nonnull_ref_or_enomem(new (nothrow) Klass(IPC::Transport(move(socket)), forward<Args>(args)...)); \
|
||||
}
|
||||
|
||||
template<typename ClientEndpoint, typename ServerEndpoint>
|
||||
class ConnectionToServer : public IPC::Connection<ClientEndpoint, ServerEndpoint>
|
||||
, public ClientEndpoint::Stub
|
||||
, public ServerEndpoint::template Proxy<ClientEndpoint> {
|
||||
public:
|
||||
using ClientStub = typename ClientEndpoint::Stub;
|
||||
using IPCProxy = typename ServerEndpoint::template Proxy<ClientEndpoint>;
|
||||
|
||||
ConnectionToServer(ClientStub& local_endpoint, Transport transport)
|
||||
: Connection<ClientEndpoint, ServerEndpoint>(local_endpoint, move(transport))
|
||||
, ServerEndpoint::template Proxy<ClientEndpoint>(*this, {})
|
||||
{
|
||||
}
|
||||
|
||||
virtual void die() override
|
||||
{
|
||||
// Override this function if you don't want your app to exit if it loses the connection.
|
||||
exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
156
Libraries/LibIPC/Decoder.cpp
Normal file
156
Libraries/LibIPC/Decoder.cpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/Proxy.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/File.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
ErrorOr<size_t> Decoder::decode_size()
|
||||
{
|
||||
return static_cast<size_t>(TRY(decode<u32>()));
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<String> decode(Decoder& decoder)
|
||||
{
|
||||
auto length = TRY(decoder.decode_size());
|
||||
return String::from_stream(decoder.stream(), length);
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<ByteString> decode(Decoder& decoder)
|
||||
{
|
||||
auto length = TRY(decoder.decode_size());
|
||||
if (length == 0)
|
||||
return ByteString::empty();
|
||||
|
||||
return ByteString::create_and_overwrite(length, [&](Bytes bytes) -> ErrorOr<void> {
|
||||
TRY(decoder.decode_into(bytes));
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<ByteBuffer> decode(Decoder& decoder)
|
||||
{
|
||||
auto length = TRY(decoder.decode_size());
|
||||
if (length == 0)
|
||||
return ByteBuffer {};
|
||||
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(length));
|
||||
auto bytes = buffer.bytes();
|
||||
|
||||
TRY(decoder.decode_into(bytes));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<JsonValue> decode(Decoder& decoder)
|
||||
{
|
||||
auto json = TRY(decoder.decode<ByteString>());
|
||||
return JsonValue::from_string(json);
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<AK::Duration> decode(Decoder& decoder)
|
||||
{
|
||||
auto nanoseconds = TRY(decoder.decode<i64>());
|
||||
return AK::Duration::from_nanoseconds(nanoseconds);
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<UnixDateTime> decode(Decoder& decoder)
|
||||
{
|
||||
auto nanoseconds = TRY(decoder.decode<i64>());
|
||||
return AK::UnixDateTime::from_nanoseconds_since_epoch(nanoseconds);
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<URL::URL> decode(Decoder& decoder)
|
||||
{
|
||||
auto url_string = TRY(decoder.decode<ByteString>());
|
||||
URL::URL url { url_string };
|
||||
|
||||
bool has_blob_url = TRY(decoder.decode<bool>());
|
||||
if (!has_blob_url)
|
||||
return url;
|
||||
|
||||
url.set_blob_url_entry(URL::BlobURLEntry {
|
||||
.type = TRY(decoder.decode<String>()),
|
||||
.byte_buffer = TRY(decoder.decode<ByteBuffer>()),
|
||||
.environment_origin = TRY(decoder.decode<URL::Origin>()),
|
||||
});
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<URL::Origin> decode(Decoder& decoder)
|
||||
{
|
||||
auto scheme = TRY(decoder.decode<ByteString>());
|
||||
auto host = TRY(decoder.decode<URL::Host>());
|
||||
auto port = TRY(decoder.decode<Optional<u16>>());
|
||||
|
||||
return URL::Origin { move(scheme), move(host), port };
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<File> decode(Decoder& decoder)
|
||||
{
|
||||
auto file = TRY(decoder.files().try_dequeue());
|
||||
auto fd = file.fd();
|
||||
|
||||
auto fd_flags = TRY(Core::System::fcntl(fd, F_GETFD));
|
||||
TRY(Core::System::fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC));
|
||||
return file;
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<Empty> decode(Decoder&)
|
||||
{
|
||||
return Empty {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<Core::AnonymousBuffer> decode(Decoder& decoder)
|
||||
{
|
||||
if (auto valid = TRY(decoder.decode<bool>()); !valid)
|
||||
return Core::AnonymousBuffer {};
|
||||
|
||||
auto size = TRY(decoder.decode_size());
|
||||
auto anon_file = TRY(decoder.decode<IPC::File>());
|
||||
|
||||
return Core::AnonymousBuffer::create_from_anon_fd(anon_file.take_fd(), size);
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<Core::DateTime> decode(Decoder& decoder)
|
||||
{
|
||||
auto timestamp = TRY(decoder.decode<i64>());
|
||||
return Core::DateTime::from_timestamp(static_cast<time_t>(timestamp));
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<Core::ProxyData> decode(Decoder& decoder)
|
||||
{
|
||||
auto type = TRY(decoder.decode<Core::ProxyData::Type>());
|
||||
auto host_ipv4 = TRY(decoder.decode<u32>());
|
||||
auto port = TRY(decoder.decode<int>());
|
||||
|
||||
return Core::ProxyData { type, host_ipv4, port };
|
||||
}
|
||||
|
||||
}
|
212
Libraries/LibIPC/Decoder.h
Normal file
212
Libraries/LibIPC/Decoder.h
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <AK/Queue.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Try.h>
|
||||
#include <AK/TypeList.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCore/SharedCircularQueue.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibIPC/Concepts.h>
|
||||
#include <LibIPC/File.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
#include <LibIPC/Message.h>
|
||||
#include <LibURL/Origin.h>
|
||||
#include <LibURL/URL.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<typename T>
|
||||
inline ErrorOr<T> decode(Decoder&)
|
||||
{
|
||||
static_assert(DependentFalse<T>, "Base IPC::decoder() instantiated");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
class Decoder {
|
||||
public:
|
||||
Decoder(Stream& stream, Queue<IPC::File>& files)
|
||||
: m_stream(stream)
|
||||
, m_files(files)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<T> decode();
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> decode_into(T& value)
|
||||
{
|
||||
value = TRY(m_stream.read_value<T>());
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> decode_into(Bytes bytes)
|
||||
{
|
||||
TRY(m_stream.read_until_filled(bytes));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<size_t> decode_size();
|
||||
|
||||
Stream& stream() { return m_stream; }
|
||||
Queue<IPC::File>& files() { return m_files; }
|
||||
|
||||
private:
|
||||
Stream& m_stream;
|
||||
Queue<IPC::File>& m_files;
|
||||
};
|
||||
|
||||
template<Arithmetic T>
|
||||
ErrorOr<T> decode(Decoder& decoder)
|
||||
{
|
||||
T value { 0 };
|
||||
TRY(decoder.decode_into(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template<Enum T>
|
||||
ErrorOr<T> decode(Decoder& decoder)
|
||||
{
|
||||
auto value = TRY(decoder.decode<UnderlyingType<T>>());
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<String> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<ByteString> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<ByteBuffer> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<JsonValue> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<AK::Duration> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<UnixDateTime> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<URL::URL> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<URL::Origin> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<File> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<Empty> decode(Decoder&);
|
||||
|
||||
template<Concepts::Array T>
|
||||
ErrorOr<T> decode(Decoder& decoder)
|
||||
{
|
||||
T array {};
|
||||
auto size = TRY(decoder.decode_size());
|
||||
if (size != array.size())
|
||||
return Error::from_string_literal("Array size mismatch");
|
||||
for (size_t i = 0; i < array.size(); ++i)
|
||||
array[i] = TRY(decoder.decode<typename T::ValueType>());
|
||||
return array;
|
||||
}
|
||||
|
||||
template<Concepts::Vector T>
|
||||
ErrorOr<T> decode(Decoder& decoder)
|
||||
{
|
||||
T vector;
|
||||
|
||||
auto size = TRY(decoder.decode_size());
|
||||
TRY(vector.try_ensure_capacity(size));
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
auto value = TRY(decoder.decode<typename T::ValueType>());
|
||||
vector.unchecked_append(move(value));
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
template<Concepts::HashMap T>
|
||||
ErrorOr<T> decode(Decoder& decoder)
|
||||
{
|
||||
T hashmap;
|
||||
|
||||
auto size = TRY(decoder.decode_size());
|
||||
TRY(hashmap.try_ensure_capacity(size));
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
auto key = TRY(decoder.decode<typename T::KeyType>());
|
||||
auto value = TRY(decoder.decode<typename T::ValueType>());
|
||||
TRY(hashmap.try_set(move(key), move(value)));
|
||||
}
|
||||
|
||||
return hashmap;
|
||||
}
|
||||
|
||||
template<Concepts::SharedSingleProducerCircularQueue T>
|
||||
ErrorOr<T> decode(Decoder& decoder)
|
||||
{
|
||||
auto anon_file = TRY(decoder.decode<IPC::File>());
|
||||
return T::create(anon_file.take_fd());
|
||||
}
|
||||
|
||||
template<Concepts::Optional T>
|
||||
ErrorOr<T> decode(Decoder& decoder)
|
||||
{
|
||||
if (auto has_value = TRY(decoder.decode<bool>()); !has_value)
|
||||
return T {};
|
||||
return T { TRY(decoder.decode<typename T::ValueType>()) };
|
||||
}
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template<Concepts::Variant T, size_t Index = 0>
|
||||
ErrorOr<T> decode_variant(Decoder& decoder, size_t index)
|
||||
{
|
||||
using ElementList = TypeList<T>;
|
||||
|
||||
if constexpr (Index < ElementList::size) {
|
||||
if (index == Index) {
|
||||
using ElementType = typename ElementList::template Type<Index>;
|
||||
return T { TRY(decoder.decode<ElementType>()) };
|
||||
}
|
||||
|
||||
return decode_variant<T, Index + 1>(decoder, index);
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<Concepts::Variant T>
|
||||
ErrorOr<T> decode(Decoder& decoder)
|
||||
{
|
||||
auto index = TRY(decoder.decode<typename T::IndexType>());
|
||||
return Detail::decode_variant<T>(decoder, index);
|
||||
}
|
||||
|
||||
// This must be last so that it knows about the above specializations.
|
||||
template<typename T>
|
||||
ErrorOr<T> Decoder::decode()
|
||||
{
|
||||
return IPC::decode<T>(*this);
|
||||
}
|
||||
|
||||
}
|
172
Libraries/LibIPC/Encoder.cpp
Normal file
172
Libraries/LibIPC/Encoder.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/BitCast.h>
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/Proxy.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <LibIPC/File.h>
|
||||
#include <LibURL/Origin.h>
|
||||
#include <LibURL/URL.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
ErrorOr<void> Encoder::encode_size(size_t size)
|
||||
{
|
||||
if (static_cast<u64>(size) > static_cast<u64>(NumericLimits<u32>::max()))
|
||||
return Error::from_string_literal("Container exceeds the maximum allowed size");
|
||||
return encode(static_cast<u32>(size));
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, float const& value)
|
||||
{
|
||||
return encoder.encode(bit_cast<u32>(value));
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, double const& value)
|
||||
{
|
||||
return encoder.encode(bit_cast<u64>(value));
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, String const& value)
|
||||
{
|
||||
auto bytes = value.bytes();
|
||||
TRY(encoder.encode_size(bytes.size()));
|
||||
TRY(encoder.append(bytes.data(), bytes.size()));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, StringView const& value)
|
||||
{
|
||||
// NOTE: Do not change this encoding without also updating LibC/netdb.cpp.
|
||||
if (value.is_null())
|
||||
return encoder.encode(NumericLimits<u32>::max());
|
||||
|
||||
TRY(encoder.encode_size(value.length()));
|
||||
TRY(encoder.append(reinterpret_cast<u8 const*>(value.characters_without_null_termination()), value.length()));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, ByteString const& value)
|
||||
{
|
||||
return encoder.encode(value.view());
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, ByteBuffer const& value)
|
||||
{
|
||||
TRY(encoder.encode_size(value.size()));
|
||||
TRY(encoder.append(value.data(), value.size()));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, JsonValue const& value)
|
||||
{
|
||||
return encoder.encode(value.serialized<StringBuilder>());
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, AK::Duration const& value)
|
||||
{
|
||||
return encoder.encode(value.to_nanoseconds());
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, UnixDateTime const& value)
|
||||
{
|
||||
return encoder.encode(value.nanoseconds_since_epoch());
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, URL::URL const& value)
|
||||
{
|
||||
TRY(encoder.encode(value.serialize()));
|
||||
|
||||
if (!value.blob_url_entry().has_value())
|
||||
return encoder.encode(false);
|
||||
|
||||
TRY(encoder.encode(true));
|
||||
|
||||
auto const& blob = value.blob_url_entry().value();
|
||||
|
||||
TRY(encoder.encode(blob.type));
|
||||
TRY(encoder.encode(blob.byte_buffer));
|
||||
TRY(encoder.encode(blob.environment_origin));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, URL::Origin const& origin)
|
||||
{
|
||||
TRY(encoder.encode<ByteString>(origin.scheme()));
|
||||
TRY(encoder.encode(origin.host()));
|
||||
TRY(encoder.encode(origin.port()));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, File const& file)
|
||||
{
|
||||
int fd = file.take_fd();
|
||||
|
||||
TRY(encoder.append_file_descriptor(fd));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, Empty const&)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, Core::AnonymousBuffer const& buffer)
|
||||
{
|
||||
TRY(encoder.encode(buffer.is_valid()));
|
||||
|
||||
if (buffer.is_valid()) {
|
||||
TRY(encoder.encode_size(buffer.size()));
|
||||
TRY(encoder.encode(TRY(IPC::File::clone_fd(buffer.fd()))));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, Core::DateTime const& datetime)
|
||||
{
|
||||
return encoder.encode(static_cast<i64>(datetime.timestamp()));
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder& encoder, Core::ProxyData const& proxy)
|
||||
{
|
||||
TRY(encoder.encode(proxy.type));
|
||||
TRY(encoder.encode(proxy.host_ipv4));
|
||||
TRY(encoder.encode(proxy.port));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
187
Libraries/LibIPC/Encoder.h
Normal file
187
Libraries/LibIPC/Encoder.h
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCore/SharedCircularQueue.h>
|
||||
#include <LibIPC/Concepts.h>
|
||||
#include <LibIPC/File.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
#include <LibIPC/Message.h>
|
||||
#include <LibURL/Forward.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> encode(Encoder&, T const&)
|
||||
{
|
||||
static_assert(DependentFalse<T>, "Base IPC::encode() was instantiated");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
class Encoder {
|
||||
public:
|
||||
explicit Encoder(MessageBuffer& buffer)
|
||||
: m_buffer(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> encode(T const& value);
|
||||
|
||||
ErrorOr<void> extend_capacity(size_t capacity)
|
||||
{
|
||||
TRY(m_buffer.extend_data_capacity(capacity));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> append(u8 const* values, size_t count)
|
||||
{
|
||||
TRY(m_buffer.append_data(values, count));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> append_file_descriptor(int fd)
|
||||
{
|
||||
TRY(m_buffer.append_file_descriptor(fd));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> encode_size(size_t size);
|
||||
|
||||
private:
|
||||
MessageBuffer& m_buffer;
|
||||
};
|
||||
|
||||
template<Arithmetic T>
|
||||
ErrorOr<void> encode(Encoder& encoder, T const& value)
|
||||
{
|
||||
TRY(encoder.append(reinterpret_cast<u8 const*>(&value), sizeof(value)));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Enum T>
|
||||
ErrorOr<void> encode(Encoder& encoder, T const& value)
|
||||
{
|
||||
return encoder.encode(to_underlying(value));
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, float const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, double const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, String const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, StringView const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, ByteString const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, ByteBuffer const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, JsonValue const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, AK::Duration const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, UnixDateTime const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, URL::URL const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, URL::Origin const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, File const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, Empty const&);
|
||||
|
||||
template<typename T, size_t N>
|
||||
ErrorOr<void> encode(Encoder& encoder, Array<T, N> const& array)
|
||||
{
|
||||
TRY(encoder.encode_size(array.size()));
|
||||
|
||||
for (auto const& value : array)
|
||||
TRY(encoder.encode(value));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Concepts::Vector T>
|
||||
ErrorOr<void> encode(Encoder& encoder, T const& vector)
|
||||
{
|
||||
// NOTE: Do not change this encoding without also updating LibC/netdb.cpp.
|
||||
TRY(encoder.encode_size(vector.size()));
|
||||
|
||||
for (auto const& value : vector)
|
||||
TRY(encoder.encode(value));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Concepts::HashMap T>
|
||||
ErrorOr<void> encode(Encoder& encoder, T const& hashmap)
|
||||
{
|
||||
TRY(encoder.encode_size(hashmap.size()));
|
||||
|
||||
for (auto it : hashmap) {
|
||||
TRY(encoder.encode(it.key));
|
||||
TRY(encoder.encode(it.value));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Concepts::SharedSingleProducerCircularQueue T>
|
||||
ErrorOr<void> encode(Encoder& encoder, T const& queue)
|
||||
{
|
||||
TRY(encoder.encode(TRY(IPC::File::clone_fd(queue.fd()))));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Concepts::Optional T>
|
||||
ErrorOr<void> encode(Encoder& encoder, T const& optional)
|
||||
{
|
||||
TRY(encoder.encode(optional.has_value()));
|
||||
|
||||
if (optional.has_value())
|
||||
TRY(encoder.encode(optional.value()));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Concepts::Variant T>
|
||||
ErrorOr<void> encode(Encoder& encoder, T const& variant)
|
||||
{
|
||||
TRY(encoder.encode(variant.index()));
|
||||
|
||||
return variant.visit([&](auto const& value) {
|
||||
return encoder.encode(value);
|
||||
});
|
||||
}
|
||||
|
||||
// This must be last so that it knows about the above specializations.
|
||||
template<typename T>
|
||||
ErrorOr<void> Encoder::encode(T const& value)
|
||||
{
|
||||
return IPC::encode(*this, value);
|
||||
}
|
||||
|
||||
}
|
86
Libraries/LibIPC/File.h
Normal file
86
Libraries/LibIPC/File.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
|
||||
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/System.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class File {
|
||||
AK_MAKE_NONCOPYABLE(File);
|
||||
|
||||
public:
|
||||
File() = default;
|
||||
|
||||
static File adopt_file(NonnullOwnPtr<Core::File> file)
|
||||
{
|
||||
return File(file->leak_fd(Badge<File> {}));
|
||||
}
|
||||
|
||||
static File adopt_fd(int fd)
|
||||
{
|
||||
return File(fd);
|
||||
}
|
||||
|
||||
static ErrorOr<File> clone_fd(int fd)
|
||||
{
|
||||
int new_fd = TRY(Core::System::dup(fd));
|
||||
return File(new_fd);
|
||||
}
|
||||
|
||||
File(File&& other)
|
||||
: m_fd(exchange(other.m_fd, -1))
|
||||
{
|
||||
}
|
||||
|
||||
File& operator=(File&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_fd = exchange(other.m_fd, -1);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~File()
|
||||
{
|
||||
if (m_fd != -1)
|
||||
(void)Core::System::close(m_fd);
|
||||
}
|
||||
|
||||
int fd() const { return m_fd; }
|
||||
|
||||
// NOTE: This is 'const' since generated IPC messages expose all parameters by const reference.
|
||||
[[nodiscard]] int take_fd() const
|
||||
{
|
||||
return exchange(m_fd, -1);
|
||||
}
|
||||
|
||||
// FIXME: IPC::Files transferred over the wire are always set O_CLOEXEC during decoding.
|
||||
// Perhaps we should add an option to IPC::File to allow the receiver to decide whether to
|
||||
// make it O_CLOEXEC or not. Or an attribute in the .ipc file?
|
||||
ErrorOr<void> clear_close_on_exec()
|
||||
{
|
||||
auto fd_flags = TRY(Core::System::fcntl(m_fd, F_GETFD));
|
||||
fd_flags &= ~FD_CLOEXEC;
|
||||
TRY(Core::System::fcntl(m_fd, F_SETFD, fd_flags));
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
explicit File(int fd)
|
||||
: m_fd(fd)
|
||||
{
|
||||
}
|
||||
|
||||
mutable int m_fd { -1 };
|
||||
};
|
||||
|
||||
}
|
26
Libraries/LibIPC/Forward.h
Normal file
26
Libraries/LibIPC/Forward.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class Decoder;
|
||||
class Encoder;
|
||||
class Message;
|
||||
class MessageBuffer;
|
||||
class File;
|
||||
class Stub;
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> encode(Encoder&, T const&);
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<T> decode(Decoder&);
|
||||
|
||||
}
|
62
Libraries/LibIPC/Message.cpp
Normal file
62
Libraries/LibIPC/Message.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <LibIPC/Message.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
using MessageSizeType = u32;
|
||||
|
||||
MessageBuffer::MessageBuffer()
|
||||
{
|
||||
m_data.resize(sizeof(MessageSizeType));
|
||||
}
|
||||
|
||||
ErrorOr<void> MessageBuffer::extend_data_capacity(size_t capacity)
|
||||
{
|
||||
TRY(m_data.try_ensure_capacity(m_data.size() + capacity));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> MessageBuffer::append_data(u8 const* values, size_t count)
|
||||
{
|
||||
TRY(m_data.try_append(values, count));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> MessageBuffer::append_file_descriptor(int fd)
|
||||
{
|
||||
auto auto_fd = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) AutoCloseFileDescriptor(fd)));
|
||||
TRY(m_fds.try_append(move(auto_fd)));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> MessageBuffer::transfer_message(Transport& transport)
|
||||
{
|
||||
Checked<MessageSizeType> checked_message_size { m_data.size() };
|
||||
checked_message_size -= sizeof(MessageSizeType);
|
||||
|
||||
if (checked_message_size.has_overflow())
|
||||
return Error::from_string_literal("Message is too large for IPC encoding");
|
||||
|
||||
MessageSizeType const message_size = checked_message_size.value();
|
||||
m_data.span().overwrite(0, reinterpret_cast<u8 const*>(&message_size), sizeof(message_size));
|
||||
|
||||
auto raw_fds = Vector<int, 1> {};
|
||||
auto num_fds_to_transfer = m_fds.size();
|
||||
if (num_fds_to_transfer > 0) {
|
||||
raw_fds.ensure_capacity(num_fds_to_transfer);
|
||||
for (auto& owned_fd : m_fds) {
|
||||
raw_fds.unchecked_append(owned_fd->value());
|
||||
}
|
||||
}
|
||||
|
||||
TRY(transport.transfer(m_data.span(), raw_fds));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
76
Libraries/LibIPC/Message.h
Normal file
76
Libraries/LibIPC/Message.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibIPC/Transport.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class AutoCloseFileDescriptor : public RefCounted<AutoCloseFileDescriptor> {
|
||||
public:
|
||||
AutoCloseFileDescriptor(int fd)
|
||||
: m_fd(fd)
|
||||
{
|
||||
}
|
||||
|
||||
~AutoCloseFileDescriptor()
|
||||
{
|
||||
if (m_fd != -1)
|
||||
close(m_fd);
|
||||
}
|
||||
|
||||
int value() const { return m_fd; }
|
||||
|
||||
private:
|
||||
int m_fd;
|
||||
};
|
||||
|
||||
class MessageBuffer {
|
||||
public:
|
||||
MessageBuffer();
|
||||
|
||||
ErrorOr<void> extend_data_capacity(size_t capacity);
|
||||
ErrorOr<void> append_data(u8 const* values, size_t count);
|
||||
|
||||
ErrorOr<void> append_file_descriptor(int fd);
|
||||
|
||||
ErrorOr<void> transfer_message(Transport& socket);
|
||||
|
||||
private:
|
||||
Vector<u8, 1024> m_data;
|
||||
Vector<NonnullRefPtr<AutoCloseFileDescriptor>, 1> m_fds;
|
||||
};
|
||||
|
||||
enum class ErrorCode : u32 {
|
||||
PeerDisconnected
|
||||
};
|
||||
|
||||
template<typename Value>
|
||||
using IPCErrorOr = ErrorOr<Value, ErrorCode>;
|
||||
|
||||
class Message {
|
||||
public:
|
||||
virtual ~Message() = default;
|
||||
|
||||
virtual u32 endpoint_magic() const = 0;
|
||||
virtual int message_id() const = 0;
|
||||
virtual char const* message_name() const = 0;
|
||||
virtual bool valid() const = 0;
|
||||
virtual ErrorOr<MessageBuffer> encode() const = 0;
|
||||
|
||||
protected:
|
||||
Message() = default;
|
||||
};
|
||||
|
||||
}
|
50
Libraries/LibIPC/MultiServer.h
Normal file
50
Libraries/LibIPC/MultiServer.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Function.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<typename ConnectionFromClientType>
|
||||
class MultiServer {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<MultiServer>> try_create(Optional<ByteString> socket_path = {})
|
||||
{
|
||||
auto server = TRY(Core::LocalServer::try_create());
|
||||
TRY(server->take_over_from_system_server(socket_path.value_or({})));
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) MultiServer(move(server)));
|
||||
}
|
||||
|
||||
static ErrorOr<NonnullOwnPtr<MultiServer>> try_create(NonnullRefPtr<Core::LocalServer> server)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) MultiServer(move(server)));
|
||||
}
|
||||
|
||||
Function<void(ConnectionFromClientType&)> on_new_client;
|
||||
|
||||
private:
|
||||
explicit MultiServer(NonnullRefPtr<Core::LocalServer> server)
|
||||
: m_server(move(server))
|
||||
{
|
||||
m_server->on_accept = [&](auto client_socket) {
|
||||
auto client_id = ++m_next_client_id;
|
||||
|
||||
auto client = IPC::new_client_connection<ConnectionFromClientType>(IPC::Transport(move(client_socket)), client_id);
|
||||
if (on_new_client)
|
||||
on_new_client(*client);
|
||||
};
|
||||
}
|
||||
|
||||
int m_next_client_id { 0 };
|
||||
RefPtr<Core::LocalServer> m_server;
|
||||
};
|
||||
|
||||
}
|
22
Libraries/LibIPC/SingleServer.h
Normal file
22
Libraries/LibIPC/SingleServer.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCore/System.h>
|
||||
#include <LibCore/SystemServerTakeover.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<typename ConnectionFromClientType>
|
||||
ErrorOr<NonnullRefPtr<ConnectionFromClientType>> take_over_accepted_client_from_system_server()
|
||||
{
|
||||
auto socket = TRY(Core::take_over_socket_from_system_server());
|
||||
return IPC::new_client_connection<ConnectionFromClientType>(IPC::Transport(move(socket)));
|
||||
}
|
||||
|
||||
}
|
35
Libraries/LibIPC/Stub.h
Normal file
35
Libraries/LibIPC/Stub.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
namespace AK {
|
||||
class BufferStream;
|
||||
}
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class Stub {
|
||||
public:
|
||||
virtual ~Stub() = default;
|
||||
|
||||
virtual u32 magic() const = 0;
|
||||
virtual ByteString name() const = 0;
|
||||
virtual ErrorOr<OwnPtr<MessageBuffer>> handle(Message const&) = 0;
|
||||
|
||||
protected:
|
||||
Stub() = default;
|
||||
|
||||
private:
|
||||
ByteString m_name;
|
||||
};
|
||||
|
||||
}
|
24
Libraries/LibIPC/Transport.h
Normal file
24
Libraries/LibIPC/Transport.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Platform.h>
|
||||
|
||||
#if !defined(AK_OS_WINDOWS)
|
||||
# include <LibIPC/TransportSocket.h>
|
||||
#endif
|
||||
|
||||
namespace IPC {
|
||||
|
||||
#if !defined(AK_OS_WINDOWS)
|
||||
// Unix Domain Sockets
|
||||
using Transport = TransportSocket;
|
||||
#else
|
||||
# error "LibIPC Transport has not been ported to this platform"
|
||||
#endif
|
||||
|
||||
}
|
151
Libraries/LibIPC/TransportSocket.cpp
Normal file
151
Libraries/LibIPC/TransportSocket.cpp
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibIPC/File.h>
|
||||
#include <LibIPC/TransportSocket.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
TransportSocket::TransportSocket(NonnullOwnPtr<Core::LocalSocket> socket)
|
||||
: m_socket(move(socket))
|
||||
{
|
||||
socklen_t socket_buffer_size = 128 * KiB;
|
||||
(void)Core::System::setsockopt(m_socket->fd().value(), SOL_SOCKET, SO_SNDBUF, &socket_buffer_size, sizeof(socket_buffer_size));
|
||||
(void)Core::System::setsockopt(m_socket->fd().value(), SOL_SOCKET, SO_RCVBUF, &socket_buffer_size, sizeof(socket_buffer_size));
|
||||
}
|
||||
|
||||
TransportSocket::~TransportSocket() = default;
|
||||
|
||||
void TransportSocket::set_up_read_hook(Function<void()> hook)
|
||||
{
|
||||
VERIFY(m_socket->is_open());
|
||||
m_socket->on_ready_to_read = move(hook);
|
||||
}
|
||||
|
||||
bool TransportSocket::is_open() const
|
||||
{
|
||||
return m_socket->is_open();
|
||||
}
|
||||
|
||||
void TransportSocket::close()
|
||||
{
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
void TransportSocket::wait_until_readable()
|
||||
{
|
||||
auto maybe_did_become_readable = m_socket->can_read_without_blocking(-1);
|
||||
if (maybe_did_become_readable.is_error()) {
|
||||
dbgln("TransportSocket::wait_until_readable: {}", maybe_did_become_readable.error());
|
||||
warnln("TransportSocket::wait_until_readable: {}", maybe_did_become_readable.error());
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
VERIFY(maybe_did_become_readable.value());
|
||||
}
|
||||
|
||||
ErrorOr<void> TransportSocket::transfer(ReadonlyBytes bytes_to_write, Vector<int, 1> const& unowned_fds)
|
||||
{
|
||||
auto num_fds_to_transfer = unowned_fds.size();
|
||||
while (!bytes_to_write.is_empty()) {
|
||||
ErrorOr<ssize_t> maybe_nwritten = 0;
|
||||
if (num_fds_to_transfer > 0) {
|
||||
maybe_nwritten = m_socket->send_message(bytes_to_write, 0, unowned_fds);
|
||||
if (!maybe_nwritten.is_error())
|
||||
num_fds_to_transfer = 0;
|
||||
} else {
|
||||
maybe_nwritten = m_socket->write_some(bytes_to_write);
|
||||
}
|
||||
|
||||
if (maybe_nwritten.is_error()) {
|
||||
if (auto error = maybe_nwritten.release_error(); error.is_errno() && (error.code() == EAGAIN || error.code() == EWOULDBLOCK)) {
|
||||
|
||||
// FIXME: Refactor this to pass the unwritten bytes back to the caller to send 'later'
|
||||
// or next time the socket is writable
|
||||
Vector<struct pollfd, 1> pollfds;
|
||||
if (pollfds.is_empty())
|
||||
pollfds.append({ .fd = m_socket->fd().value(), .events = POLLOUT, .revents = 0 });
|
||||
|
||||
ErrorOr<int> result { 0 };
|
||||
do {
|
||||
constexpr u32 POLL_TIMEOUT_MS = 100;
|
||||
result = Core::System::poll(pollfds, POLL_TIMEOUT_MS);
|
||||
} while (result.is_error() && result.error().code() == EINTR);
|
||||
|
||||
if (!result.is_error() && result.value() != 0)
|
||||
continue;
|
||||
|
||||
switch (error.code()) {
|
||||
case EPIPE:
|
||||
return Error::from_string_literal("IPC::transfer_message: Disconnected from peer");
|
||||
case EAGAIN:
|
||||
return Error::from_string_literal("IPC::transfer_message: Timed out waiting for socket to become writable");
|
||||
default:
|
||||
return Error::from_syscall("IPC::transfer_message write"sv, -error.code());
|
||||
}
|
||||
} else {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_to_write = bytes_to_write.slice(maybe_nwritten.value());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
TransportSocket::ReadResult TransportSocket::read_as_much_as_possible_without_blocking(Function<void()> schedule_shutdown)
|
||||
{
|
||||
u8 buffer[4096];
|
||||
|
||||
ReadResult result;
|
||||
auto received_fds = Vector<int> {};
|
||||
auto& bytes = result.bytes;
|
||||
|
||||
while (is_open()) {
|
||||
auto maybe_bytes_read = m_socket->receive_message({ buffer, 4096 }, MSG_DONTWAIT, received_fds);
|
||||
if (maybe_bytes_read.is_error()) {
|
||||
auto error = maybe_bytes_read.release_error();
|
||||
if (error.is_syscall() && error.code() == EAGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (error.is_syscall() && error.code() == ECONNRESET) {
|
||||
schedule_shutdown();
|
||||
break;
|
||||
}
|
||||
|
||||
dbgln("TransportSocket::read_as_much_as_possible_without_blocking: {}", error);
|
||||
warnln("TransportSocket::read_as_much_as_possible_without_blocking: {}", error);
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
auto bytes_read = maybe_bytes_read.release_value();
|
||||
if (bytes_read.is_empty()) {
|
||||
schedule_shutdown();
|
||||
break;
|
||||
}
|
||||
|
||||
bytes.append(bytes_read.data(), bytes_read.size());
|
||||
result.fds.append(received_fds.data(), received_fds.size());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorOr<int> TransportSocket::release_underlying_transport_for_transfer()
|
||||
{
|
||||
return m_socket->release_fd();
|
||||
}
|
||||
|
||||
ErrorOr<IPC::File> TransportSocket::clone_for_transfer()
|
||||
{
|
||||
return IPC::File::clone_fd(m_socket->fd().value());
|
||||
}
|
||||
|
||||
}
|
44
Libraries/LibIPC/TransportSocket.h
Normal file
44
Libraries/LibIPC/TransportSocket.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCore/File.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class TransportSocket {
|
||||
AK_MAKE_NONCOPYABLE(TransportSocket);
|
||||
AK_MAKE_DEFAULT_MOVABLE(TransportSocket);
|
||||
|
||||
public:
|
||||
explicit TransportSocket(NonnullOwnPtr<Core::LocalSocket> socket);
|
||||
~TransportSocket();
|
||||
|
||||
void set_up_read_hook(Function<void()>);
|
||||
bool is_open() const;
|
||||
void close();
|
||||
|
||||
void wait_until_readable();
|
||||
|
||||
ErrorOr<void> transfer(ReadonlyBytes, Vector<int, 1> const& unowned_fds);
|
||||
|
||||
struct [[nodiscard]] ReadResult {
|
||||
Vector<u8> bytes;
|
||||
Vector<int> fds;
|
||||
};
|
||||
ReadResult read_as_much_as_possible_without_blocking(Function<void()> schedule_shutdown);
|
||||
|
||||
// Obnoxious name to make it clear that this is a dangerous operation.
|
||||
ErrorOr<int> release_underlying_transport_for_transfer();
|
||||
|
||||
ErrorOr<IPC::File> clone_for_transfer();
|
||||
|
||||
private:
|
||||
NonnullOwnPtr<Core::LocalSocket> m_socket;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue