LibCore: Support launching a process with an IPC connection

Much of the code here is adapted from Ladybird. But rather than doing a
manual fork, we use Core::Process to spawn the underlying process.
This commit is contained in:
Timothy Flynn 2024-04-23 16:35:36 -04:00 committed by Andrew Kaster
parent dc52404aec
commit 9986350e97
2 changed files with 60 additions and 2 deletions

View file

@ -2,6 +2,7 @@
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2023-2024, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -13,6 +14,7 @@
#include <LibCore/Environment.h>
#include <LibCore/File.h>
#include <LibCore/Process.h>
#include <LibCore/Socket.h>
#include <LibCore/System.h>
#include <errno.h>
#include <spawn.h>
@ -351,4 +353,27 @@ ErrorOr<bool> Process::wait_for_termination()
return exited_with_code_0;
}
ErrorOr<IPCProcess::ProcessAndIPCSocket> IPCProcess::spawn_and_connect_to_process(ProcessSpawnOptions const& options)
{
int socket_fds[2] {};
TRY(System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
ArmedScopeGuard guard_fd_0 { [&] { MUST(System::close(socket_fds[0])); } };
ArmedScopeGuard guard_fd_1 { [&] { MUST(System::close(socket_fds[1])); } };
auto& file_actions = const_cast<Vector<ProcessSpawnOptions::FileActionType>&>(options.file_actions);
file_actions.append(FileAction::CloseFile { socket_fds[0] });
auto takeover_string = MUST(String::formatted("{}:{}", options.name, socket_fds[1]));
TRY(Environment::set("SOCKET_TAKEOVER"sv, takeover_string, Environment::Overwrite::Yes));
auto process = TRY(Process::spawn(options));
auto ipc_socket = TRY(LocalSocket::adopt_fd(socket_fds[0]));
guard_fd_0.disarm();
TRY(ipc_socket->set_blocking(true));
return ProcessAndIPCSocket { move(process), move(ipc_socket) };
}
}

View file

@ -2,6 +2,7 @@
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -33,11 +34,14 @@ struct CloseFile {
}
struct ProcessSpawnOptions {
ByteString executable;
StringView name {};
ByteString executable {};
bool search_for_executable_in_path { false };
Vector<ByteString> const& arguments {};
Optional<ByteString> working_directory {};
Vector<Variant<FileAction::OpenFile, FileAction::CloseFile>> const& file_actions {};
using FileActionType = Variant<FileAction::OpenFile, FileAction::CloseFile>;
Vector<FileActionType> const& file_actions {};
};
class Process {
@ -99,4 +103,33 @@ private:
bool m_should_disown;
};
class IPCProcess {
public:
template<typename ClientType>
struct ProcessAndIPCClient {
Process process;
NonnullRefPtr<ClientType> client;
};
template<typename ClientType, typename... ClientArguments>
static ErrorOr<ProcessAndIPCClient<ClientType>> spawn(ProcessSpawnOptions const& options, ClientArguments&&... client_arguments)
{
auto [process, socket] = TRY(spawn_and_connect_to_process(options));
auto client = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ClientType { move(socket), forward<ClientArguments>(client_arguments)... }));
return ProcessAndIPCClient<ClientType> { move(process), move(client) };
}
pid_t pid() const { return m_process.pid(); }
private:
struct ProcessAndIPCSocket {
Process process;
NonnullOwnPtr<Core::LocalSocket> m_ipc_socket;
};
static ErrorOr<ProcessAndIPCSocket> spawn_and_connect_to_process(ProcessSpawnOptions const& options);
Process m_process;
};
}