rx/Process: add spawn, waitProcess, suspendProcess & resumeProcess utils

This commit is contained in:
DH 2025-12-25 21:58:06 +03:00
parent d8fdf8209e
commit 8587801745
2 changed files with 194 additions and 0 deletions

View file

@ -1,6 +1,8 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace rx {
using ProcessId = std::uint32_t;
@ -27,4 +29,21 @@ ProcessId getCurrentProcessId();
ThreadId getCurrentThreadId();
ProcessHandle getCurrentProcessHandle();
ThreadHandle getCurrentThreadHandle();
// Spawn a new process with the given executable and arguments
// Returns the process ID on success, or -1 on failure
ProcessId spawn(const std::string &executable,
const std::vector<std::string> &args = {});
// Wait for a process to exit and return its exit code
// Returns true if the process exited normally, false otherwise
bool waitProcess(ProcessId pid, int *exitCode = nullptr);
// Suspend a process
// Returns true on success, false otherwise
bool suspendProcess(ProcessId pid);
// Resume a suspended process
// Returns true on success, false otherwise
bool resumeProcess(ProcessId pid);
} // namespace rx

View file

@ -1,9 +1,13 @@
#include <rx/Process.hpp>
#include <vector>
#include <memory>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <sys/wait.h>
#include <unistd.h>
#endif
@ -48,3 +52,174 @@ rx::ThreadHandle rx::getCurrentThreadHandle() {
return getCurrentThreadId();
#endif
}
rx::ProcessId rx::spawn(const std::string &executable,
const std::vector<std::string> &args) {
#ifdef _WIN32
// Build command line
std::string cmdLine = "\"" + executable + "\"";
for (const auto &arg : args) {
cmdLine += " \"" + arg + "\"";
}
// Convert to wide string
int wideSize =
::MultiByteToWideChar(CP_UTF8, 0, cmdLine.c_str(), -1, nullptr, 0);
if (wideSize <= 0) {
return static_cast<ProcessId>(-1);
}
std::vector<wchar_t> wideCmdLine(wideSize);
::MultiByteToWideChar(CP_UTF8, 0, cmdLine.c_str(), -1, wideCmdLine.data(),
wideSize);
STARTUPINFOW si{};
si.cb = sizeof(si);
PROCESS_INFORMATION pi{};
if (!::CreateProcessW(nullptr, wideCmdLine.data(), nullptr, nullptr, FALSE, 0,
nullptr, nullptr, &si, &pi)) {
return static_cast<ProcessId>(-1);
}
ProcessId pid = ::GetProcessId(pi.hProcess);
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
return pid;
#else
// fork + execv
pid_t pid = ::fork();
if (pid < 0) {
return static_cast<ProcessId>(-1);
}
if (pid == 0) {
// child process
std::vector<char *> argv;
argv.reserve(args.size() + 2);
argv.push_back(const_cast<char *>(executable.c_str()));
// Add arguments
for (const auto &arg : args) {
argv.push_back(const_cast<char *>(arg.c_str()));
}
argv.push_back(nullptr);
::execv(executable.c_str(), argv.data());
// If execv returns, it failed
::_exit(127);
}
// parent process
return static_cast<ProcessId>(pid);
#endif
}
bool rx::waitProcess(ProcessId pid, int *exitCode) {
#ifdef _WIN32
HANDLE hProcess = ::OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
FALSE, static_cast<DWORD>(pid));
if (!hProcess) {
return false;
}
DWORD result = ::WaitForSingleObject(hProcess, INFINITE);
if (result != WAIT_OBJECT_0) {
::CloseHandle(hProcess);
return false;
}
if (exitCode) {
DWORD code = 0;
if (::GetExitCodeProcess(hProcess, &code)) {
*exitCode = static_cast<int>(code);
} else {
*exitCode = -1;
}
}
::CloseHandle(hProcess);
return true;
#else
int status = 0;
pid_t result = ::waitpid(static_cast<pid_t>(pid), &status, 0);
if (result == -1) {
return false;
}
if (exitCode) {
if (WIFEXITED(status)) {
*exitCode = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
*exitCode = 128 + WTERMSIG(status);
} else {
*exitCode = -1;
}
}
return WIFEXITED(status) || WIFSIGNALED(status);
#endif
}
bool rx::suspendProcess(ProcessId pid) {
#ifdef _WIN32
// Use NtSuspendProcess from ntdll.dll
using NtSuspendProcessFn = long(__stdcall *)(HANDLE);
static NtSuspendProcessFn ntSuspend = [] {
if (auto mod = ::GetModuleHandleW(L"ntdll.dll")) {
return reinterpret_cast<NtSuspendProcessFn>(
::GetProcAddress(mod, "NtSuspendProcess"));
}
return static_cast<NtSuspendProcessFn>(nullptr);
}();
if (!ntSuspend) {
return false;
}
HANDLE hProcess =
::OpenProcess(PROCESS_SUSPEND_RESUME, FALSE, static_cast<DWORD>(pid));
if (!hProcess) {
return false;
}
long status = ntSuspend(hProcess);
::CloseHandle(hProcess);
return status >= 0;
#else
return ::kill(static_cast<pid_t>(pid), SIGSTOP) == 0;
#endif
}
bool rx::resumeProcess(ProcessId pid) {
#ifdef _WIN32
// Use NtResumeProcess from ntdll.dll
using NtResumeProcessFn = long(__stdcall *)(HANDLE);
static NtResumeProcessFn ntResume = [] {
if (auto mod = ::GetModuleHandleW(L"ntdll.dll")) {
return reinterpret_cast<NtResumeProcessFn>(
::GetProcAddress(mod, "NtResumeProcess"));
}
return static_cast<NtResumeProcessFn>(nullptr);
}();
if (!ntResume) {
return false;
}
HANDLE hProcess =
::OpenProcess(PROCESS_SUSPEND_RESUME, FALSE, static_cast<DWORD>(pid));
if (!hProcess) {
return false;
}
long status = ntResume(hProcess);
::CloseHandle(hProcess);
return status >= 0;
#else
return ::kill(static_cast<pid_t>(pid), SIGCONT) == 0;
#endif
}