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