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)
This commit is contained in:
Reece Wilson 2022-01-29 11:49:11 +00:00
parent a6f96315f3
commit 19ebdf3761
29 changed files with 735 additions and 471 deletions

View File

@ -22,14 +22,14 @@ namespace Aurora::Crypto::AES
// Initialization vectors could be derived from SHA1, Tiger, or SHA2 digests
AUKN_SYM bool Encrypt(const Memory::MemoryViewRead &plainText,
const Memory::MemoryViewRead &inIv,
const Memory::MemoryViewWrite &outIv,
const Memory::MemoryViewWrite &outIv, // optional
const Memory::MemoryViewRead &inKey,
Memory::ByteBuffer &out,
bool auCoolCodePadding);
AUKN_SYM bool Decrypt(const Memory::MemoryViewRead &cipherText,
const Memory::MemoryViewRead &inIv,
const Memory::MemoryViewWrite &outIv,
const Memory::MemoryViewWrite &outIv, // optional
const Memory::MemoryViewRead &inKey,
Memory::ByteBuffer &plainText,
bool auCoolCodePadding);

View File

@ -18,25 +18,20 @@ namespace Aurora::Debug
/**
Retrieves a print-friendly callstack of the last trap (either innocent exception or fatal mem access) <br>
On Win32, this information is always available <br>
On other platforms, this function will likely yield no valuable information
On Win32, this information is always available
*/
AUKN_SYM AuString GetLastErrorStack();
AUKN_SYM StackTrace GetLastStackTrace();
AUKN_SYM StackTrace GetStackTrace();
/**
Retrieve information about the last exception. <br>
On Win32, this information is always available <br>
On other platforms, this information is only available within C++14 catch blocks
Retrieve information about the last exception.
On Win32, this information is always available
*/
AUKN_SYM AuString GetLastException();
/**
Retrieve the last system error (IE: Win32, GetLastError())
Retrieve the last system error (Example -> Win32, GetLastError())
*/
AUKN_SYM OSError_t GetLastSystemMessage();
@ -50,9 +45,12 @@ namespace Aurora::Debug
*/
AUKN_SYM void PrintError();
AUKN_SYM void CheckErrors();
AUKN_SYM StackTrace GetStackTrace();
/**
Immediately terminates the process.
May attempt some hardened telemetry debug ops

View File

@ -12,7 +12,7 @@ namespace Aurora::Hashing
class IHashStream
{
public:
/*
/**
* Digest length of pBuf bytes
*/
virtual void Ingest(const void *pBuf, AuUInt32 length) = 0;

View File

@ -0,0 +1,12 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Character.hpp
Date: 2022-1-29
Author: Reece
***/
#pragma once
#include "ICharacterProvider.hpp"
#include "IBufferedCharacterConsumer.hpp"
#include "ILineBufferer.hpp"

View File

@ -0,0 +1,13 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: IBufferedCharacterConsumer.hpp
Date: 2022-1-29
Author: Reece
***/
#pragma once
namespace Aurora::IO::Character
{
}

View File

@ -0,0 +1,15 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: ICharacterProvider.hpp
Date: 2022-1-29
Author: Reece
***/
#pragma once
namespace Aurora::IO::Character
{
AUKN_INTERFACE(ICharacterProvider,
AUI_METHOD(AuUInt8, GetByte, ())
);
}

View File

@ -0,0 +1,13 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: ILineBufferer.hpp
Date: 2022-1-29
Author: Reece
***/
#pragma once
namespace Aurora::IO::Character
{
}

View File

@ -33,7 +33,7 @@ namespace Aurora::IO::Net
eIPProtocolV6
));
struct IPAddress
struct AUKN_SYM IPAddress
{
EIPProtocol ip;
union
@ -41,11 +41,18 @@ namespace Aurora::IO::Net
AuUInt8 v4[4];
AuUInt16 v6[8];
};
IPAddress();
IPAddress(const AuString &parse);
AUKN_SYM IPAddress(const AuString &parse);
AUKN_SYM AuString ToString();
AUKN_SYM bool IsValid();
AuString ToString() const;
bool IsValid() const;
inline operator bool() const
{
return IsValid();
}
inline bool operator ==(const IPAddress &cmp) const
{
@ -268,7 +275,7 @@ namespace Aurora::IO::Net
// Attempts to seek backwards or forwards in the UDP or TCP packet
// If you are under the callstack of a HasXXXHasData callback, you are guaranteed (bufferedReadSize - streamPosition - streamRemaining) bytes backwards
//
virtual bool SeekAsync(int signedDistanceFromCur = 0);
virtual bool SeekAsync(int signedDistanceFromCur = 0) = 0;
// When BufferInputStreamAdhoc is called with false / in the default condition, read sync will be constrained by the async buffer
// When BufferInputStreamAdhoc is called with true, you will block until you can get the requested data (all of it if, all = true; any of it, all = false)
@ -415,18 +422,42 @@ namespace Aurora::IO::Net
virtual AuSPtr<ISocketFactory> GetSocketFactory() = 0;
};
struct AUKN_SYM WorkRange
{
WorkRange();
AuUInt8 workerOffsetOrAny;
AuUInt8 workerCountOrAny;
};
struct AUKN_SYM WorkPoolGroup
{
WorkPoolGroup();
AuSPtr<Async::IThreadPool> pool; // optional -> defaults to async apps core loop
AuUInt8 asyncWorkGroup;
AuUInt8 asyncWorkPoolOffsetOrAny;
WorkRange range;
};
struct INetworkingPool
{
// A:
virtual AuUInt32 Pump(int idx) = 0;
virtual AuUInt32 Pump(AuUInt8 workerId) = 0;
// B:
virtual AuUInt32 PumpRead(int idx) = 0;
virtual AuUInt32 PumpWrite(int idx) = 0;
virtual AuUInt32 PumpRead(AuUInt8 workerId) = 0;
virtual AuUInt32 PumpWrite(AuUInt8 workerId) = 0;
// C:
virtual AuUInt32 PollWorker(int idx) = 0;
virtual AuUInt32 RunWorker(int idx, AuUInt32 timeout) = 0;
virtual AuUInt32 PollWorker(AuUInt8 workerId) = 0;
virtual AuUInt32 RunWorker(AuUInt8 workerId, AuUInt32 timeout) = 0;
// D: class APIs
virtual bool BeginReadPollingOnWorkQueues(const WorkPoolGroup &workGroup) = 0;
virtual bool BeginSubmissionsOnOnWorkQueues(const WorkPoolGroup &workGroup) = 0;
virtual void StopPollingOnWorkQueues() = 0;
virtual AuUInt8 GetWorkers() = 0;
@ -435,34 +466,10 @@ namespace Aurora::IO::Net
virtual void Shutdown() = 0;
};
AUE_DEFINE(ENetworkPoolModel, (
// Do not attach to a thread subsystem
eNoHooks,
/// given a group id, uses prespawned async application event queues
eAsyncApp,
/// it just werks
eInternalThreadPool,
/// Developer intends to call the INetworkingPoll poll functions with the respective thread id
eUserCreateThreadRunnable
))
struct NetworkPool
{
AuUInt8 workers {1};
ENetworkPoolModel mode;
AuUInt8 asyncWorkGroup {};
AuUInt8 asyncWorkerIdOffset {};
AuSPtr<Async::IThreadPool> threadPool;
/// Ignore me. Used on platforms that suck (win32) when the model is defined as eAsyncApp or eAsyncThreadPool
AuUInt32 frequencyNotAsync {50};
};
AUKN_SHARED_API(CreateNetworkPool, INetworkingPool, const NetworkPool & meta);

View File

@ -21,6 +21,6 @@ namespace Aurora::Parse
AUKN_SYM void ByteToHex(AuUInt8 val, char(&hex)[2]);
AUKN_SYM bool HexToInt (const char *hex, AuUInt32 length, AuUInt64 &val);
AUKN_SYM void EncodeHex(const void *pBuf, AuUInt32 length, EHexDump formatting, AuString &out);
AUKN_SYM bool EncodeHex(const void *pBuf, AuUInt32 length, EHexDump formatting, AuString &out);
AUKN_SYM bool DecodeHex(const AuString &in, Memory::ByteBuffer &out);
}

View File

@ -9,10 +9,13 @@
namespace Aurora::Processes
{
enum class ESpawnType
{
eSpawnAtomicOvermap, // posix: exec, win32: i dunno
eSpawnChildProcessWorker, // posix: thread, win32: job process
eSpawnThreadLeader // posix: thread leader / new sid / pid != 0 daemon, win32: process
};
AUE_DEFINE(ESpawnType,
(
// posix: exec, win32: i dunno
eSpawnAtomicOvermap,
// posix: thread, win32: job process
eSpawnChildProcessWorker,
// posix: thread leader / new sid / pid != 0 daemon, win32: process
eSpawnThreadLeader
));
}

View File

@ -0,0 +1,11 @@
#pragma once
namespace Aurora::Processes
{
AUE_DEFINE(EStandardHandle,
(
eStdIn,
eStdOut,
eStdError
));
}

View File

@ -28,9 +28,7 @@ namespace Aurora::Processes
virtual AuUInt GetProcessId() = 0;
///
// TODO(Reece): what in the hell this is ugly
virtual bool Start(enum ESpawnType, bool fwdOut, bool fwdErr, bool fwdIn) = 0;
virtual bool Start() = 0;
virtual bool Terminate() = 0;
virtual bool TryKill() = 0;

View File

@ -8,7 +8,9 @@
#pragma once
#include "ESpawnType.hpp"
#include "EStandardHandle.hpp"
#include "IProcess.hpp"
#include "StartupParmaters.hpp"
#include "Spawn.hpp"
#include "UtilRun.hpp"

View File

@ -9,5 +9,5 @@
namespace Aurora::Processes
{
AUKN_SHARED_API(Spawn, IProcess, const AuString &app, const AuList<AuString> &args);
AUKN_SHARED_API(Spawn, IProcess, const StartupParmaters &startup);
}

View File

@ -0,0 +1,15 @@
#pragma once
namespace Aurora::Processes
{
struct StartupParmaters
{
AuString process;
AuList<AuString> args;
enum ESpawnType type;
bool fwdOut {};
bool fwdErr {};
bool fwdIn {};
bool noShowConsole {}; // Hides conhost or otherwise blanks stdin/out handles
};
}

View File

@ -10,8 +10,9 @@
#include <Source/Telemetry/Telemetry.hpp>
#if defined(AURORA_PLATFORM_WIN32)
#include "ExceptionWatcher.NT.hpp"
#include "ExceptionWatcher.Win32.hpp"
#include "ExceptionWatcher.NT.hpp"
#include "ExceptionWatcher.Win32.hpp"
#include "Stack.Win32.hpp"
#endif
namespace Aurora::Debug
@ -200,7 +201,11 @@ namespace Aurora::Debug
AUKN_SYM StackTrace GetStackTrace()
{
#if defined(AURORA_PLATFORM_WIN32)
return PlatformWalkCallStack();
#else
return {};
#endif
}
AUKN_SYM AuString GetLastException()

View File

@ -138,7 +138,6 @@ namespace Aurora::Debug
return message;
}
static void HandleFatal(bool fatal, _EXCEPTION_POINTERS *pExceptionInfo)
{
static bool handlingFatal = false;

View File

@ -127,5 +127,4 @@ namespace Aurora::Debug
ParseStack(&ctx, ret);
return ret;
}
}
}

View File

View File

View File

@ -193,7 +193,7 @@ namespace Aurora::Parse
return true;
}
AUKN_SYM void EncodeHex(const void *pBuf, AuUInt32 length, EHexDump formatting, AuString &in)
AUKN_SYM bool EncodeHex(const void *pBuf, AuUInt32 length, EHexDump formatting, AuString &in)
{
bool hexedit = formatting == EHexDump::eHexEditor;
@ -206,100 +206,111 @@ namespace Aurora::Parse
bool space = formatting != EHexDump::eString;
in.reserve(length * 4);
auto &newLine = AuLocale::NewLine();
if (hexedit)
try
{
in.insert(in.size(), "00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15");
in.insert(in.size(), newLine);
in.insert(in.size(), "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
in.insert(in.size(), newLine);
in.insert(in.size(), " ");
in.insert(in.size(), newLine);
}
else if (squareBracket)
{
in.insert(in.end(), '[');
in.insert(in.size(), newLine);
}
else if (curlyBracket)
{
in.insert(in.end(), '{');
in.insert(in.size(), newLine);
}
for (int i = 0; i < length; )
{
AuUInt32 x, rowMax;
if (i != 0)
{
if (hexedit || squareBracket || curlyBracket)
{
in.insert(in.size(), newLine);
}
}
in.reserve(length * 4);
if (squareBracket || curlyBracket)
{
in.insert(in.size(), " ");
}
rowMax = AuMin(AuUInt32(i + 16), AuUInt32(length));
for (x = i;
x < rowMax;
x ++)
{
if (space)
{
if (x != i)
{
in.insert(in.end(), ' ');
}
}
if (zeroX)
{
in.insert(in.size(), "0x");
}
{
char hex[2];
ByteToHex(reinterpret_cast<const AuUInt8*>(pBuf)[x], hex);
in.insert(in.size(), hex, 2);
}
if (x != (length - 1))
{
if (squareBracket || curlyBracket)
{
in.insert(in.end(), ',');
}
}
}
i = x;
auto &newLine = AuLocale::NewLine();
if (hexedit)
{
// TODO: print space + i * 16 padded + space hex(i * 16 padded)
in.insert(in.size(), "00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15");
in.insert(in.size(), newLine);
in.insert(in.size(), "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
in.insert(in.size(), newLine);
in.insert(in.size(), " ");
in.insert(in.size(), newLine);
}
else if (squareBracket)
{
in.insert(in.end(), '[');
in.insert(in.size(), newLine);
}
else if (curlyBracket)
{
in.insert(in.end(), '{');
in.insert(in.size(), newLine);
}
}
if (squareBracket)
{
in.insert(in.size(), newLine);
in.insert(in.end(), ']');
}
else if (curlyBracket)
{
in.insert(in.size(), newLine);
in.insert(in.end(), '}');
}
for (int i = 0; i < length; )
{
AuUInt32 x, rowMax;
in.shrink_to_fit();
if (i != 0)
{
if (hexedit || squareBracket || curlyBracket)
{
in.insert(in.size(), newLine);
}
}
if (squareBracket || curlyBracket)
{
in.insert(in.size(), " ");
}
rowMax = AuMin(AuUInt32(i + 16), AuUInt32(length));
for (x = i;
x < rowMax;
x++)
{
if (space)
{
if (x != i)
{
in.insert(in.end(), ' ');
}
}
if (zeroX)
{
in.insert(in.size(), "0x");
}
{
char hex[2];
ByteToHex(reinterpret_cast<const AuUInt8 *>(pBuf)[x], hex);
in.insert(in.size(), hex, 2);
}
if (x != (length - 1))
{
if (squareBracket || curlyBracket)
{
in.insert(in.end(), ',');
}
}
}
i = x;
if (hexedit)
{
// TODO: print space + i * 16 padded + space hex(i * 16 padded)
}
}
if (squareBracket)
{
in.insert(in.size(), newLine);
in.insert(in.end(), ']');
}
else if (curlyBracket)
{
in.insert(in.size(), newLine);
in.insert(in.end(), '}');
}
in.shrink_to_fit();
}
catch (...)
{
SysPushErrorGen("EncodeHex failed");
in.clear();
return false;
}
return true;
}
}

View File

@ -0,0 +1,404 @@
/***
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);
}
}

View File

@ -0,0 +1,13 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Process.NT.hpp
Date: 2022-1-29
Author: Reece
***/
#pragma once
namespace Aurora::Processes
{
}

View File

@ -1,12 +1,11 @@
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
Copyright (C) 2021-2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Process.Win32.cpp
Date: 2021-6-12
Date: 2022-1-29
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "Processes.hpp"
#include "Process.Win32.hpp"
#include <shellapi.h>
#include <tlhelp32.h>
@ -18,7 +17,7 @@ namespace Aurora::Processes
void InitWin32()
{
gLeaderJob = CreateJobObject(NULL, NULL);
gLeaderJob = CreateJobObject(NULL, NULL);
if (!gLeaderJob)
{
SysPushErrorArg("CreateJobObject error");
@ -38,118 +37,41 @@ namespace Aurora::Processes
AuWin32CloseHandle(gLeaderJob);
}
class ProcessImpl : public IProcess
void AssignJobWorker(HANDLE handle)
{
public:
ProcessImpl(AuString execModule, AuList<AuString> args);
~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;
// TODO: what in the hell this is ugly
bool Start(enum ESpawnType type, bool fwdOut, bool fwdErr, bool fwdIn) override;
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};
AuString execModule_;
ESpawnType type_;
AuList<AuString> args_;
AuList<const char *> cargs_;
AuString windowsCli_;
AuThreads::ThreadUnique_t thread_;
HANDLE process_ {INVALID_HANDLE_VALUE};
HANDLE hthread_ {INVALID_HANDLE_VALUE};
AuSInt exitCode_;
};
ProcessImpl::ProcessImpl(AuString execModule, AuList<AuString> args) : execModule_(execModule), args_(args)
{
this->args_.insert(this->args_.begin(), execModule);
// 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->args_)
if (gLeaderJob)
{
this->cargs_.push_back(arg.c_str());
this->windowsCli_ += arg + " ";
if (!AssignProcessToJobObject(gLeaderJob, handle))
{
SysPushErrorGen("Could not AssignProcessToObject");
}
}
this->cargs_.push_back(nullptr);
this->windowsCli_.resize(this->windowsCli_.size() - 1);
}
ProcessImpl::~ProcessImpl()
static bool HasWin32ProcessExited(HANDLE handle)
{
if (this->type_ == ESpawnType::eSpawnChildProcessWorker)
DWORD exitCode;
if (!GetExitCodeProcess(handle, &exitCode))
{
TryKill();
Terminate();
return true;
}
if (this->thread_)
{
this->thread_.reset();
}
AuWin32CloseHandle(this->process_);
AuWin32CloseHandle(this->hthread_);
ShutdownPipes();
return exitCode != STILL_ACTIVE;
}
AuUInt ProcessImpl::GetProcessId()
{
if (this->process_ == INVALID_HANDLE_VALUE)
{
return {};
}
return ::GetProcessId(this->process_);
}
static BOOL TermWinHandleWin32Thread(HWND handle, LPARAM a)
{
SendMessageA(handle, WM_CLOSE, 0, 0);
(*reinterpret_cast<AuUInt32 *>(a))++;
return true;
}
bool ProcessImpl::TermWinEnumProcesses()
static bool SendExtermianteWinloopCloseMessages(HANDLE handle)
{
THREADENTRY32 te{};
HANDLE h{};
if (this->process_ == INVALID_HANDLE_VALUE)
{
return false;
}
h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetProcessId());
h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetProcessId(handle));
if (h == INVALID_HANDLE_VALUE)
{
return false;
@ -176,230 +98,58 @@ namespace Aurora::Processes
{
return false;
}
return Threading::YieldPoll(true, 2500, [=]()
{
return !HasExited();
});
return true;
}
bool ProcessImpl::TryKill()
static bool SendControlCEquiv(HANDLE handle)
{
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)
if (!(AttachConsole(GetProcessId(handle))))
{
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);
if (SetConsoleCtrlHandler({}, true))
{
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
SysAssert(FreeConsole(), "Couldn't pop off zombie process console");
Sleep(750);
}
else
{
SysAssert(FreeConsole(), "Couldn't pop off zombie process console");
return false;
}
auto handle = errorStream ? pipeStdErrRead_ : pipeStdOutRead_;
if (!SetConsoleCtrlHandler({}, true))
{
SysPushErrorHAL("Couldn't swap control+c handler -> wont accept control termination on exit");
}
return true;
}
static bool Wait2500OrUntilClose(HANDLE handle)
{
return Threading::YieldPoll(true, 2500, [=]()
{
return !HasWin32ProcessExited(handle);
});
}
bool SendExitSignal(HANDLE handle)
{
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::Start(enum ESpawnType type, bool fwdOut, bool fwdErr, bool fwdIn)
{
this->exitCode_ = 0x10110100;
this->type_ = type;
if (type == ESpawnType::eSpawnAtomicOvermap)
{
_spawnv(_P_OVERLAY, this->execModule_.c_str(), this->cargs_.data());
SysPushErrorGen("_spawnv didn't overwrite the process map, given {} ({})", this->execModule_, this->windowsCli_);
return false;
}
{
SECURITY_ATTRIBUTES saAttr{};
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
if (fwdOut)
{
if (!CreatePipe(&pipeStdOutRead_, &pipeStdOutWrite_, &saAttr, 0))
{
return false;
}
if (!SetHandleInformation(pipeStdOutRead_, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
}
if (fwdErr)
{
if (!CreatePipe(&pipeStdErrRead_, &pipeStdErrWrite_, &saAttr, 0))
{
return false;
}
if (!SetHandleInformation(pipeStdErrRead_, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
}
if (fwdIn)
{
if (!CreatePipe(&pipeStdInRead_, &pipeStdInWrite_, &saAttr, 0))
{
return false;
}
if (!SetHandleInformation(pipeStdInWrite_, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
}
}
PROCESS_INFORMATION processInfo = { 0 };
{
STARTUPINFOW startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
bool inheritHandles = fwdIn || fwdErr || fwdOut;
startupInfo.hStdInput = pipeStdInRead_;
startupInfo.hStdError = pipeStdErrWrite_;
startupInfo.hStdOutput = pipeStdOutWrite_;
startupInfo.dwFlags |= (inheritHandles ? STARTF_USESTDHANDLES : 0);
auto result = CreateProcessW(Locale::ConvertFromUTF8(this->execModule_).c_str(),
Locale::ConvertFromUTF8(this->windowsCli_).data(),
NULL, NULL, inheritHandles,
NULL,
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 (type == ESpawnType::eSpawnChildProcessWorker)
{
if (gLeaderJob)
{
if (!AssignProcessToJobObject(gLeaderJob, processInfo.hProcess))
{
SysPushErrorGen("Could not AssignProcessToObject");
}
}
}
}
// 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_)
if (!(SendControlCEquiv(handle) // send GenerateConsoleCtrlEvent
|| SendExtermianteWinloopCloseMessages(handle))) // send WM_CLOSE
{
return false;
}
this->thread_->Run();
return true;
}
AUKN_SYM IProcess *SpawnNew(const AuString &app, const AuList<AuString> &args)
{
return _new ProcessImpl(app, args);
}
AUKN_SYM void SpawnRelease(IProcess *process)
{
SafeDelete<ProcessImpl *>(process);
return Wait2500OrUntilClose(handle);
}
}

View File

@ -1,14 +1,16 @@
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Process.Win32.hpp
Date: 2021-6-12
Date: 2022-1-29
Author: Reece
***/
#pragma once
namespace Aurora::Processes
{
void InitWin32();
void DeinitWin32();
}
void InitWin32();
void AssignJobWorker(HANDLE handle);
bool SendExitSignal(HANDLE handle);
}

View File

@ -12,7 +12,7 @@ using high_res_clock = std::chrono::high_resolution_clock;
using sys_clock = std::chrono::system_clock;
#if defined(AURORA_PLATFORM_WIN32)
#define timegm _mkgmtime
#define timegm _mkgmtime
#endif
static sys_clock::duration gEpoch;
@ -20,13 +20,7 @@ static sys_clock::duration gUnixDelta;
static auto InitEpoch()
{
std::tm start{};
start.tm_mday = 29; // day number
start.tm_mon = 7; // month idx, aug
start.tm_year = 101; // 1900 + 101
start.tm_hour = 9; // 11 - index - DST
start.tm_min = 15; // minute offset
std::tm start{0, 15, 10, 29, 7, 101, 0, 0, 0};
auto epoch = sys_clock::from_time_t(timegm(&start)).time_since_epoch();
std::tm unixStart{};

View File

@ -16,7 +16,7 @@ namespace Aurora::Time
ULARGE_INTEGER ull;
ull.LowPart = ft.dwLowDateTime;
ull.HighPart = ft.dwHighDateTime;
return ull.QuadPart / 10000ULL - 12'643'550'100'000ULL;
return ull.QuadPart / 10000ULL - 12'643'553'700'000ULL;
}