AuroraRuntime/Source/Processes/Process.NT.cpp
Reece 19ebdf3761 Preparing for initial WSA network POC rewrite, not porting gen1 code. Linux support is way behind. Will work on it soon.
[*] Minor refactors
[*] Begin refactoring the Processes subsystem
[*] Added empty files ready to move the gross c++ functional callback in the parse subsystem to a dedicated interface w/ utils
[*] Broke out Win32 Process into an NT base variant (Process.NT.cpp) with Win32 overloaded process exit pokes (Process.Win32.cpp)
2022-01-29 12:36:25 +00:00

404 lines
12 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Process.NT.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "Processes.hpp"
#include "Process.Win32.hpp"
#include <process.h>
#if defined(AURORA_PLATFORM_WIN32)
#include "Process.Win32.hpp"
#endif
namespace Aurora::Processes
{
class ProcessImpl : public IProcess
{
public:
ProcessImpl(const StartupParmaters &params);
~ProcessImpl();
bool TermWinEnumProcesses();
bool TryKill() override;
bool HasExited();
AuUInt GetProcessId() override;
bool Terminate() override;
AuSPtr<Threading::IWaitable> AsWaitable() override;
AuSInt GetExitCode() override;
void ShutdownPipes();
bool Read(bool error, void *buffer, AuUInt32 &len) override;
bool Read(void *buffer, AuUInt32 &len, bool errorStream, bool nonblock) override;
bool Write(const void *buffer, AuUInt32 len) override;
bool Start() override;
bool Init();
private:
HANDLE pipeStdOutRead_ {INVALID_HANDLE_VALUE};
HANDLE pipeStdOutWrite_ {INVALID_HANDLE_VALUE};
HANDLE pipeStdErrRead_ {INVALID_HANDLE_VALUE};
HANDLE pipeStdErrWrite_ {INVALID_HANDLE_VALUE};
HANDLE pipeStdInRead_ {INVALID_HANDLE_VALUE};
HANDLE pipeStdInWrite_ {INVALID_HANDLE_VALUE};
StartupParmaters startup_;
ESpawnType type_;
AuList<const char *> cargs_;
AuString windowsCli_;
AuThreads::ThreadUnique_t thread_;
HANDLE process_ {INVALID_HANDLE_VALUE};
HANDLE hthread_ {INVALID_HANDLE_VALUE};
AuSInt exitCode_;
};
ProcessImpl::ProcessImpl(const StartupParmaters &params) : startup_(params)
{
startup_.args.insert(startup_.args.begin(), startup_.process);
// ehhhh https://github.com/tritao/WindowsSDK/blob/07983c7ba4f6861d15e23f195744c60c0c249ce0/SDKs/SourceDir/Windows%20Kits/10/Source/10.0.17763.0/ucrt/exec/cenvarg.cpp#L23
for (const auto &arg : this->startup_.args)
{
this->cargs_.push_back(arg.c_str());
this->windowsCli_ += arg + " ";
}
this->cargs_.push_back(nullptr);
this->windowsCli_.resize(this->windowsCli_.size() - 1);
this->type_ = params.type;
}
ProcessImpl::~ProcessImpl()
{
if (this->type_ == ESpawnType::eSpawnChildProcessWorker)
{
TryKill();
Terminate();
}
if (this->thread_)
{
this->thread_.reset();
}
AuWin32CloseHandle(this->process_);
AuWin32CloseHandle(this->hthread_);
ShutdownPipes();
}
AuUInt ProcessImpl::GetProcessId()
{
if (this->process_ == INVALID_HANDLE_VALUE)
{
return {};
}
return ::GetProcessId(this->process_);
}
bool ProcessImpl::TermWinEnumProcesses()
{
#if defined(AURORA_PLATFORM_WIN32)
return SendExitSignal(this->process_);
#else
return false;
#endif
}
bool ProcessImpl::TryKill()
{
return TermWinEnumProcesses();
}
bool ProcessImpl::HasExited()
{
DWORD a;
if (!GetExitCodeProcess(this->process_, &a)) return true;
return a != STILL_ACTIVE;
}
bool ProcessImpl::Terminate()
{
if (this->process_ != INVALID_HANDLE_VALUE)
{
return false;
}
return TerminateProcess(this->process_, 0) || HasExited();
}
AuSPtr<Threading::IWaitable> ProcessImpl::AsWaitable()
{
if (!this->thread_) return nullptr;
return this->thread_->AsWaitable();
}
AuSInt ProcessImpl::GetExitCode()
{
return this->exitCode_;
}
void ProcessImpl::ShutdownPipes()
{
AuWin32CloseHandle(this->pipeStdOutRead_);
AuWin32CloseHandle(this->pipeStdOutWrite_);
AuWin32CloseHandle(this->pipeStdErrRead_);
AuWin32CloseHandle(this->pipeStdErrWrite_);
AuWin32CloseHandle(this->pipeStdInRead_);
AuWin32CloseHandle(this->pipeStdInWrite_);
}
bool ProcessImpl::Read(bool error, void *buffer, AuUInt32 &len)
{
return Read(buffer, len, error, false);
}
bool ProcessImpl::Read(void *buffer, AuUInt32 &len, bool errorStream, bool nonblock)
{
DWORD size = AuExchange(len, 0);
auto handle = errorStream ? pipeStdErrRead_ : pipeStdOutRead_;
if (handle == INVALID_HANDLE_VALUE)
{
return false;
}
if (nonblock)
{
DWORD avail {};
if (!PeekNamedPipe(handle, NULL, NULL, NULL, &avail, NULL))
{
return false;
}
if (!avail)
{
return true;
}
size = AuMin(size, avail);
}
auto ret = ReadFile(handle, buffer, size, &size, NULL);
len = size;
return ret;
}
bool ProcessImpl::Write(const void *buffer, AuUInt32 len)
{
DWORD size = len;
if (pipeStdInWrite_ == INVALID_HANDLE_VALUE) return false;
return WriteFile(pipeStdInWrite_, buffer, size, &size, NULL) && size == len;
}
bool ProcessImpl::Init()
{
SECURITY_ATTRIBUTES saAttr {};
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
if (this->type_ == ESpawnType::eSpawnAtomicOvermap)
{
return true;
}
if (this->process_ != INVALID_HANDLE_VALUE)
{
return false;
}
this->exitCode_ = 0x10110100;
if (this->startup_.fwdOut)
{
if (!CreatePipe(&pipeStdOutRead_, &pipeStdOutWrite_, &saAttr, 0))
{
return false;
}
if (!SetHandleInformation(pipeStdOutRead_, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
}
if (this->startup_.fwdErr)
{
if (!CreatePipe(&pipeStdErrRead_, &pipeStdErrWrite_, &saAttr, 0))
{
return false;
}
if (!SetHandleInformation(pipeStdErrRead_, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
}
if (this->startup_.fwdIn)
{
if (!CreatePipe(&pipeStdInRead_, &pipeStdInWrite_, &saAttr, 0))
{
return false;
}
if (!SetHandleInformation(pipeStdInWrite_, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
}
if (this->startup_.noShowConsole)
{
HANDLE nulFile = INVALID_HANDLE_VALUE;
// I dont want to move the other SetHandleInformations down below the if block above is, just to find out double SetHandleInfo with the same input params results in a return false condition
// This will do for.now
#define CHK_NUL \
if (nulFile == INVALID_HANDLE_VALUE) \
{ \
nulFile = CreateFileA("nul:", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); \
SetHandleInformation(nulFile, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); \
}
if (this->pipeStdInRead_ == INVALID_HANDLE_VALUE)
{
CHK_NUL;
this->pipeStdInRead_ = nulFile;
}
if (this->pipeStdErrWrite_ == INVALID_HANDLE_VALUE)
{
CHK_NUL;
this->pipeStdErrWrite_ = nulFile;
}
if (this->pipeStdOutWrite_ == INVALID_HANDLE_VALUE)
{
CHK_NUL;
this->pipeStdOutWrite_ = nulFile;
}
}
return true;
}
bool ProcessImpl::Start()
{
if (this->process_ != INVALID_HANDLE_VALUE)
{
return false;
}
if (this->type_ == ESpawnType::eSpawnAtomicOvermap)
{
_spawnv(_P_OVERLAY, this->startup_.process.c_str(), this->cargs_.data());
SysPushErrorGen("_spawnv didn't overwrite the process map, given {} ({})", this->startup_.process, this->windowsCli_);
return false;
}
PROCESS_INFORMATION processInfo = { 0 };
{
STARTUPINFOW startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
bool inheritHandles = this->startup_.fwdIn || this->startup_.fwdErr || this->startup_.fwdOut || this->startup_.noShowConsole;
startupInfo.hStdInput = pipeStdInRead_;
startupInfo.hStdError = pipeStdErrWrite_;
startupInfo.hStdOutput = pipeStdOutWrite_;
startupInfo.dwFlags |= (inheritHandles ? STARTF_USESTDHANDLES : 0);
auto result = CreateProcessW(Locale::ConvertFromUTF8(this->startup_.process).c_str(),
Locale::ConvertFromUTF8(this->windowsCli_).data(),
NULL, NULL, inheritHandles,
this->startup_.noShowConsole ? CREATE_NO_WINDOW : NULL, // yea we can keep CREATE_NO_WINDOW on for non-console apps. its legal -> https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
NULL, NULL, &startupInfo, &processInfo);
if (!result)
{
SysPushErrorGen("CreateProcess failed");
return false;
}
this->process_ = processInfo.hProcess;
this->hthread_ = processInfo.hThread;
AuWin32CloseHandle(this->pipeStdOutRead_);
AuWin32CloseHandle(this->pipeStdErrRead_);
AuWin32CloseHandle(this->pipeStdInWrite_);
if (this->type_ == ESpawnType::eSpawnChildProcessWorker)
{
#if defined(AURORA_PLATFORM_WIN32)
AssignJobWorker(processInfo.hProcess);
#endif
}
}
// TODO: delegate to a singular worker thread
auto a = [=]()
{
WaitForSingleObject(processInfo.hProcess, INFINITE);
DWORD exitCode;
auto result = GetExitCodeProcess(processInfo.hProcess, &exitCode);
this->exitCode_ = exitCode;
};
this->thread_ = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(a)),
AuThreads::IThreadVectorsFunctional::OnExit_t{})
));
if (!this->thread_)
{
return false;
}
this->thread_->Run();
return true;
}
AUKN_SYM IProcess *SpawnNew(const StartupParmaters &params)
{
try
{
auto hi = _new ProcessImpl(params);
if (!hi) return {};
if (!hi->Init())
{
delete hi;
return {};
}
return hi;
}
catch (...)
{
return {};
}
}
AUKN_SYM void SpawnRelease(IProcess *process)
{
SafeDelete<ProcessImpl *>(process);
}
}