/* * Copyright (c) 2021, Andreas Kling * Copyright (c) 2022, MacDue * Copyright (c) 2023, Sam Atkins * Copyright (c) 2024, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace Core { namespace FileAction { struct OpenFile { ByteString path; File::OpenMode mode = File::OpenMode::NotOpen; int fd = -1; mode_t permissions = 0600; }; struct CloseFile { int fd { -1 }; }; // FIXME: Implement other file actions } struct ProcessSpawnOptions { StringView name {}; ByteString executable {}; bool search_for_executable_in_path { false }; Vector const& arguments {}; Optional working_directory {}; using FileActionType = Variant; Vector file_actions {}; }; class IPCProcess; class Process { AK_MAKE_NONCOPYABLE(Process); public: enum class KeepAsChild { Yes, No }; Process(Process&& other) : m_pid(exchange(other.m_pid, 0)) , m_should_disown(exchange(other.m_should_disown, false)) { } Process& operator=(Process&& other) { m_pid = exchange(other.m_pid, 0); m_should_disown = exchange(other.m_should_disown, false); return *this; } ~Process() { (void)disown(); } static ErrorOr spawn(ProcessSpawnOptions const& options); // FIXME: Make the following 2 functions return Process instance or delete them. static ErrorOr spawn(StringView path, ReadonlySpan arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No); static ErrorOr spawn(StringView path, ReadonlySpan arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No); // FIXME: Remove this. char const* should not exist on this level of abstraction. static ErrorOr spawn(StringView path, ReadonlySpan arguments = {}, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No); static ErrorOr get_name(); enum class SetThreadName { No, Yes, }; static ErrorOr set_name(StringView, SetThreadName = SetThreadName::No); static void wait_for_debugger_and_break(); static ErrorOr is_being_debugged(); pid_t pid() const { return m_pid; } ErrorOr disown(); // FIXME: Make it return an exit code. ErrorOr wait_for_termination(); private: friend IPCProcess; Process(pid_t pid) : m_pid(pid) , m_should_disown(true) { } pid_t m_pid; bool m_should_disown; }; class IPCProcess { public: template struct ProcessAndIPCClient { Process process; NonnullRefPtr client; }; template static ErrorOr> 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(client_arguments)... })); return ProcessAndIPCClient { move(process), move(client) }; } template static ErrorOr> spawn_singleton(ProcessSpawnOptions const& options, ClientArguments&&... client_arguments) { auto [process, socket] = TRY(spawn_singleton_and_connect_to_process(options)); auto client = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ClientType { move(socket), forward(client_arguments)... })); return ProcessAndIPCClient { move(process), move(client) }; } struct ProcessPaths { ByteString socket_path; ByteString pid_path; }; static ErrorOr paths_for_process(StringView process_name); static ErrorOr> get_process_pid(StringView process_name, StringView pid_path); static ErrorOr create_ipc_socket(ByteString const& socket_path); pid_t pid() const { return m_process.pid(); } private: struct ProcessAndIPCSocket { Process process; NonnullOwnPtr m_ipc_socket; }; static ErrorOr spawn_and_connect_to_process(ProcessSpawnOptions const& options); static ErrorOr spawn_singleton_and_connect_to_process(ProcessSpawnOptions const& options); Process m_process; }; }