Skip to content

Commit

Permalink
LibIPC: Port to Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
stasoid committed Dec 17, 2024
1 parent 969fb1a commit c3a5079
Show file tree
Hide file tree
Showing 12 changed files with 408 additions and 57 deletions.
18 changes: 15 additions & 3 deletions Libraries/LibIPC/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@ set(SOURCES
Connection.cpp
Decoder.cpp
Encoder.cpp
Message.cpp
)

if (UNIX)
list(APPEND SOURCES TransportSocket.cpp)
list(APPEND SOURCES
File.cpp
Message.cpp
TransportSocket.cpp)
else()
list(APPEND SOURCES
FileWindows.cpp
MessageWindows.cpp
TransportSocketWindows.cpp)
endif()

serenity_lib(LibIPC ipc)
target_link_libraries(LibIPC PRIVATE LibCore LibURL LibThreading)
target_link_libraries(LibIPC PRIVATE LibCore LibThreading LibURL)

if (WIN32)
find_package(pthread REQUIRED)
target_include_directories(LibIPC PRIVATE ${PTHREAD_INCLUDE_DIR})
endif()
45 changes: 18 additions & 27 deletions Libraries/LibIPC/Concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,54 +27,45 @@ namespace IPC::Concepts {

namespace Detail {

// Cannot use SpecializationOf with these templates because they have non-type parameters. See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1985r3.pdf
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;
constexpr bool IsArray = false;
template<typename T, size_t N>
constexpr bool IsArray<Array<T, N>> = true;

template<typename T>
constexpr inline bool IsOptional = false;
constexpr bool IsVector = false;
template<typename T, size_t inline_capacity>
constexpr bool IsVector<Vector<T, inline_capacity>> = true;

template<typename T>
constexpr inline bool IsOptional<Optional<T>> = true;
constexpr bool IsHashMap = false;
template<typename K, typename V, typename KeyTraits, typename ValueTraits, bool IsOrdered>
constexpr bool IsHashMap<HashMap<K, V, KeyTraits, ValueTraits, IsOrdered>> = true;

template<typename T>
constexpr inline bool IsSharedSingleProducerCircularQueue = false;
constexpr bool IsSharedSingleProducerCircularQueue = false;
template<typename T, size_t Size>
constexpr inline bool IsSharedSingleProducerCircularQueue<Core::SharedSingleProducerCircularQueue<T, Size>> = true;
constexpr 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;
concept Array = Detail::IsArray<T>;

template<typename T>
constexpr inline bool IsArray = false;
template<typename T, size_t N>
constexpr inline bool IsArray<Array<T, N>> = true;

}
concept Vector = Detail::IsVector<T>;

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>;
concept Optional = SpecializationOf<T, AK::Optional>;

template<typename T>
concept Vector = Detail::IsVector<T>;

template<typename T>
concept Array = Detail::IsArray<T>;
concept Variant = SpecializationOf<T, AK::Variant>;

}
2 changes: 1 addition & 1 deletion Libraries/LibIPC/Connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class ConnectionBase : public Core::EventReceiver {
RefPtr<Core::Timer> m_responsiveness_timer;

Vector<NonnullOwnPtr<Message>> m_unprocessed_messages;
Queue<IPC::File> m_unprocessed_fds;
Queue<IPC::File> m_unprocessed_fds; // unused on Windows
ByteBuffer m_unprocessed_bytes;

u32 m_local_endpoint_magic { 0 };
Expand Down
12 changes: 0 additions & 12 deletions Libraries/LibIPC/Decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include <LibIPC/Decoder.h>
#include <LibIPC/File.h>
#include <LibURL/URL.h>
#include <fcntl.h>

namespace IPC {

Expand Down Expand Up @@ -118,17 +117,6 @@ ErrorOr<URL::Host> decode(Decoder& decoder)
return URL::Host { move(value) };
}

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&)
{
Expand Down
36 changes: 36 additions & 0 deletions Libraries/LibIPC/File.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2020, Sergey Bugaev <[email protected]>
* Copyright (c) 2021, Andreas Kling <[email protected]>
* Copyright (c) 2023, Tim Flynn <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibIPC/Decoder.h>
#include <LibIPC/File.h>

namespace IPC {

// 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> File::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 {};
}

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;
}

}
11 changes: 1 addition & 10 deletions Libraries/LibIPC/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,7 @@ class File {
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 {};
}
ErrorOr<void> clear_close_on_exec();

private:
explicit File(int fd)
Expand Down
42 changes: 42 additions & 0 deletions Libraries/LibIPC/FileWindows.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024, stasoid <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibIPC/Decoder.h>
#include <LibIPC/File.h>

#include <AK/Windows.h>

namespace IPC {

ErrorOr<void> File::clear_close_on_exec()
{
if (!SetHandleInformation(Core::System::fd_to_handle(m_fd), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
return Error::from_windows_error();
return {};
}

template<>
ErrorOr<File> decode(Decoder& decoder)
{
using namespace Core::System;
auto handle_type = TRY(decoder.decode<HandleType>());
intptr_t handle = 0;
if (handle_type == FileMappingHandle) {
TRY(decoder.decode_into(handle));
} else if (handle_type == SocketHandle) {
WSAPROTOCOL_INFO pi = {};
TRY(decoder.decode_into({ (u8*)&pi, sizeof(pi) }));
handle = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, &pi, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT);
if (handle == -1)
return Error::from_windows_error();
} else {
return Error::from_string_literal("Invalid handle type");
}
int fd = handle_to_fd(handle, handle_type);
return File::adopt_fd(fd);
}

}
9 changes: 6 additions & 3 deletions Libraries/LibIPC/Message.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
#include <AK/RefPtr.h>
#include <AK/Vector.h>
#include <LibCore/Forward.h>
#include <LibCore/System.h>
#include <LibIPC/Transport.h>
#include <unistd.h>

namespace IPC {

Expand All @@ -27,7 +27,7 @@ class AutoCloseFileDescriptor : public RefCounted<AutoCloseFileDescriptor> {
~AutoCloseFileDescriptor()
{
if (m_fd != -1)
close(m_fd);
(void)Core::System::close(m_fd);
}

int value() const { return m_fd; }
Expand All @@ -45,11 +45,14 @@ class MessageBuffer {

ErrorOr<void> append_file_descriptor(int fd);

ErrorOr<void> transfer_message(Transport& socket);
ErrorOr<void> transfer_message(Transport& transport);

private:
Vector<u8, 1024> m_data;
Vector<NonnullRefPtr<AutoCloseFileDescriptor>, 1> m_fds;
#ifdef AK_OS_WINDOWS
Vector<size_t> m_handle_offsets;
#endif
};

enum class ErrorCode : u32 {
Expand Down
70 changes: 70 additions & 0 deletions Libraries/LibIPC/MessageWindows.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2024, Tim Flynn <[email protected]>
* Copyright (c) 2024, stasoid <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibIPC/Message.h>

#include <AK/Windows.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)
{
using namespace Core::System;

HANDLE handle = fd_to_handle(fd);
if (handle == INVALID_HANDLE_VALUE)
return Error::from_string_literal("Invalid file descriptor");

m_fds.append(adopt_ref(*new AutoCloseFileDescriptor(fd)));
m_handle_offsets.append(m_data.size());

if (is_socket(fd)) {
HandleType type = SocketHandle;
m_data.append((u8*)&type, sizeof(type));
WSAPROTOCOL_INFO pi = {};
*(HANDLE*)&pi = handle;
// the handle will be duplicated and WSAPROTOCOL_INFO will be filled later in TransportSocketWindows::transfer
m_data.append((u8*)&pi, sizeof(pi));
} else {
HandleType type = FileMappingHandle;
m_data.append((u8*)&type, sizeof(type));
// the handle will be overwritten by a duplicate handle later in TransportSocketWindows::transfer
m_data.append((u8*)&handle, sizeof(handle));
}
return {};
}

ErrorOr<void> MessageBuffer::transfer_message(Transport& transport)
{
VERIFY(m_data.size() >= sizeof(MessageSizeType) && m_data.size() < NumericLimits<MessageSizeType>::max());
size_t message_size = m_data.size() - sizeof(MessageSizeType);
*(MessageSizeType*)m_data.data() = message_size;

TRY(transport.transfer(m_data.span(), m_handle_offsets));
return {};
}

}
4 changes: 3 additions & 1 deletion Libraries/LibIPC/Transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#if !defined(AK_OS_WINDOWS)
# include <LibIPC/TransportSocket.h>
#else
# include <LibIPC/TransportSocketWindows.h>
#endif

namespace IPC {
Expand All @@ -18,7 +20,7 @@ namespace IPC {
// Unix Domain Sockets
using Transport = TransportSocket;
#else
# error "LibIPC Transport has not been ported to this platform"
using Transport = TransportSocketWindows;
#endif

}
Loading

0 comments on commit c3a5079

Please sign in to comment.