diff --git a/Libraries/LibCore/Process.cpp b/Libraries/LibCore/Process.cpp index 73b6f18f8b3..3dde4299c5d 100644 --- a/Libraries/LibCore/Process.cpp +++ b/Libraries/LibCore/Process.cpp @@ -61,6 +61,24 @@ struct ArgvList { } }; +Process::Process(Process&& other) + : m_pid(exchange(other.m_pid, 0)) + , m_should_disown(exchange(other.m_should_disown, false)) +{ +} + +Process& Process::operator=(Process&& other) +{ + m_pid = exchange(other.m_pid, 0); + m_should_disown = exchange(other.m_should_disown, false); + return *this; +} + +Process::~Process() +{ + (void)disown(); +} + Process Process::current() { auto p = Process { getpid() }; @@ -297,6 +315,11 @@ void Process::wait_for_debugger_and_break() } } +pid_t Process::pid() const +{ + return m_pid; +} + ErrorOr Process::disown() { if (m_pid != 0 && m_should_disown) { diff --git a/Libraries/LibCore/Process.h b/Libraries/LibCore/Process.h index 23deabbd5d2..9e99ff30270 100644 --- a/Libraries/LibCore/Process.h +++ b/Libraries/LibCore/Process.h @@ -53,23 +53,9 @@ public: 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(); - } + Process(Process&& other); + Process& operator=(Process&& other); + ~Process(); static ErrorOr spawn(ProcessSpawnOptions const& options); static Process current(); @@ -87,14 +73,17 @@ public: static void wait_for_debugger_and_break(); static ErrorOr is_being_debugged(); - pid_t pid() const { return m_pid; } + pid_t pid() const; +#ifndef AK_OS_WINDOWS ErrorOr disown(); +#endif // FIXME: Make it return an exit code. ErrorOr wait_for_termination(); private: +#ifndef AK_OS_WINDOWS Process(pid_t pid = -1) : m_pid(pid) , m_should_disown(true) @@ -103,6 +92,14 @@ private: pid_t m_pid; bool m_should_disown; +#else + Process(void* handle = 0) + : m_handle(handle) + { + } + + void* m_handle; +#endif }; } diff --git a/Libraries/LibCore/ProcessWindows.cpp b/Libraries/LibCore/ProcessWindows.cpp new file mode 100644 index 00000000000..fc65784e8a6 --- /dev/null +++ b/Libraries/LibCore/ProcessWindows.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2022-2023, MacDue + * Copyright (c) 2023-2024, Sam Atkins + * Copyright (c) 2024, Tim Flynn + * Copyright (c) 2024, stasoid + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Core { + +Process::Process(Process&& other) + : m_handle(exchange(other.m_handle, nullptr)) +{ +} + +Process& Process::operator=(Process&& other) +{ + m_handle = exchange(other.m_handle, nullptr); + return *this; +} + +Process::~Process() +{ + if (m_handle) + CloseHandle(m_handle); +} + +Process Process::current() +{ + return GetCurrentProcess(); +} + +ErrorOr Process::spawn(ProcessSpawnOptions const& options) +{ + // file actions are not supported + VERIFY(options.file_actions.is_empty()); + + char const* program_path = 0; + StringBuilder builder; + if (options.search_for_executable_in_path) + builder.appendff("\"{}\" ", options.executable); + else + program_path = options.executable.characters(); + + builder.join(' ', options.arguments); + builder.append('\0'); + ByteBuffer command_line = TRY(builder.to_byte_buffer()); + + auto curdir = options.working_directory.has_value() ? options.working_directory->characters() : 0; + + STARTUPINFO startup_info = {}; + PROCESS_INFORMATION process_info = {}; + + BOOL result = CreateProcess( + program_path, + (char*)command_line.data(), + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + curdir, + &startup_info, + &process_info); + + if (!result) + return Error::from_windows_error(GetLastError()); + + CloseHandle(process_info.hThread); + + return Process(process_info.hProcess); +} + +ErrorOr Process::spawn(StringView path, ReadonlySpan arguments, ByteString working_directory, KeepAsChild) +{ + return spawn({ + .executable = path, + .arguments = Vector { arguments }, + .working_directory = working_directory.is_empty() ? Optional {} : working_directory, + }); +} + +ErrorOr Process::spawn(StringView path, ReadonlySpan arguments, ByteString working_directory, KeepAsChild) +{ + Vector backing_strings; + backing_strings.ensure_capacity(arguments.size()); + for (auto argument : arguments) + backing_strings.append(argument); + + return spawn({ + .executable = path, + .arguments = backing_strings, + .working_directory = working_directory.is_empty() ? Optional {} : working_directory, + }); +} + +// Get the full path of the executable file of the current process +ErrorOr Process::get_name() +{ + wchar_t path[MAX_PATH] = {}; + + DWORD length = GetModuleFileNameW(NULL, path, MAX_PATH); + if (!length) + return Error::from_windows_error(GetLastError()); + + return String::from_utf16(Utf16View { { (u16*)path, length } }); +} + +ErrorOr Process::set_name(StringView, SetThreadName) +{ + // Process::set_name() probably cannot be implemented on Windows. + return {}; +} + +ErrorOr Process::is_being_debugged() +{ + return IsDebuggerPresent(); +} + +// Forces the process to sleep until a debugger is attached, then breaks. +void Process::wait_for_debugger_and_break() +{ + bool print_message = true; + for (;;) { + if (IsDebuggerPresent()) { + DebugBreak(); + return; + } + if (print_message) { + dbgln("Process {} with pid {} is sleeping, waiting for debugger.", Process::get_name(), GetCurrentProcessId()); + print_message = false; + } + Sleep(100); + } +} + +pid_t Process::pid() const +{ + return GetProcessId(m_handle); +} + +ErrorOr Process::wait_for_termination() +{ + auto result = WaitForSingleObject(m_handle, INFINITE); + if (result == WAIT_FAILED) + return Error::from_windows_error(GetLastError()); + + DWORD exit_code = 0; + if (!GetExitCodeProcess(m_handle, &exit_code)) + return Error::from_windows_error(GetLastError()); + + return !exit_code; +} + +}