diff options
Diffstat (limited to 'source/core/windows/slang-win-process-util.cpp')
| -rw-r--r-- | source/core/windows/slang-win-process-util.cpp | 333 |
1 files changed, 0 insertions, 333 deletions
diff --git a/source/core/windows/slang-win-process-util.cpp b/source/core/windows/slang-win-process-util.cpp deleted file mode 100644 index 4039361a4..000000000 --- a/source/core/windows/slang-win-process-util.cpp +++ /dev/null @@ -1,333 +0,0 @@ -// slang-win-process-util.cpp -#include "../slang-process-util.h" - -#include "../slang-string.h" -#include "../slang-string-escape-util.h" - -#ifdef _WIN32 -// Include Windows header in a way that minimized namespace pollution. -// TODO: We could try to avoid including this at all, but it would -// mean trying to hide certain struct layouts, which would add -// more dynamic allocation. -# define WIN32_LEAN_AND_MEAN -# define NOMINMAX -# include <Windows.h> -# undef WIN32_LEAN_AND_MEAN -# undef NOMINMAX -#endif - -#include <stdio.h> -#include <stdlib.h> - -namespace Slang { - -namespace { // anonymous - -struct ThreadInfo -{ - HANDLE file; - String output; -}; - -// Has behavior very similar to unique_ptr - assignment is a move. -class WinHandle -{ -public: - /// Detach the encapsulated handle. Returns the handle (which now must be externally handled) - HANDLE detach() { HANDLE handle = m_handle; m_handle = nullptr; return handle; } - - /// Return as a handle - operator HANDLE() const { return m_handle; } - - /// Assign - void operator=(HANDLE handle) { setNull(); m_handle = handle; } - void operator=(WinHandle&& rhs) { HANDLE handle = m_handle; m_handle = rhs.m_handle; rhs.m_handle = handle; } - - /// Get ready for writing - SLANG_FORCE_INLINE HANDLE* writeRef() { setNull(); return &m_handle; } - /// Get for read access - SLANG_FORCE_INLINE const HANDLE* readRef() const { return &m_handle; } - - void setNull() - { - if (m_handle) - { - CloseHandle(m_handle); - m_handle = nullptr; - } - } - - /// Ctor - WinHandle(HANDLE handle = nullptr):m_handle(handle) {} - WinHandle(WinHandle&& rhs):m_handle(rhs.m_handle) { rhs.m_handle = nullptr; } - - /// Dtor - ~WinHandle() { setNull(); } - -private: - - WinHandle(const WinHandle&) = delete; - void operator=(const WinHandle& rhs) = delete; - - HANDLE m_handle; -}; - -} // anonymous - -static DWORD WINAPI _readerThreadProc(LPVOID threadParam) -{ - ThreadInfo* info = (ThreadInfo*)threadParam; - HANDLE file = info->file; - - static const int kChunkSize = 1024; - char buffer[kChunkSize]; - - StringBuilder outputBuilder; - - // We need to re-write the output to deal with line - // endings, so we check for paired '\r' and '\n' - // characters, which may span chunks. - int prevChar = -1; - - for (;;) - { - DWORD bytesRead = 0; - BOOL readResult = ReadFile(file, buffer, kChunkSize, &bytesRead, nullptr); - - const DWORD lastError = GetLastError(); - if (lastError == ERROR_BROKEN_PIPE) - { - break; - } - - if (!readResult) - { - break; - } - - - // walk the buffer and rewrite to eliminate '\r' '\n' pairs - char* readCursor = buffer; - char const* end = buffer + bytesRead; - char* writeCursor = buffer; - - while (readCursor != end) - { - int p = prevChar; - int c = *readCursor++; - prevChar = c; - switch (c) - { - case '\r': case '\n': - // swallow input if '\r' and '\n' appear in sequence - if ((p ^ c) == ('\r' ^ '\n')) - { - // but don't swallow the next byte - prevChar = -1; - continue; - } - // always replace '\r' with '\n' - c = '\n'; - break; - - default: - break; - } - - *writeCursor++ = (char)c; - } - bytesRead = (DWORD)(writeCursor - buffer); - - // Note: Current "core" implementation gives no way to know - // the length of the buffer, so we ultimately have - // to just assume null termination... - outputBuilder.Append(buffer, bytesRead); - } - - info->output = outputBuilder.ProduceString(); - - return 0; -} - -/* static */StringEscapeHandler* ProcessUtil::getEscapeHandler() -{ - return StringEscapeUtil::getHandler(StringEscapeUtil::Style::Space); -} - -/* static */UnownedStringSlice ProcessUtil::getExecutableSuffix() -{ - return UnownedStringSlice::fromLiteral(".exe"); -} - -/* static */String ProcessUtil::getCommandLineString(const CommandLine& commandLine) -{ - auto escapeHandler = getEscapeHandler(); - - StringBuilder cmd; - StringEscapeUtil::appendMaybeQuoted(escapeHandler, commandLine.m_executable.getUnownedSlice(), cmd); - - for (const auto& arg : commandLine.m_args) - { - cmd << " "; - StringEscapeUtil::appendMaybeQuoted(escapeHandler, arg.getUnownedSlice(), cmd); - } - return cmd.ToString(); -} - -#define SLANG_RETURN_FAIL_ON_FALSE(x) if (!(x)) return SLANG_FAIL; - -/* static */SlangResult ProcessUtil::execute(const CommandLine& commandLine, ExecuteResult& outExecuteResult) -{ - outExecuteResult.init(); - - SECURITY_ATTRIBUTES securityAttributes; - securityAttributes.nLength = sizeof(securityAttributes); - securityAttributes.lpSecurityDescriptor = nullptr; - securityAttributes.bInheritHandle = true; - - WinHandle childStdOutRead; - WinHandle childStdErrRead; - WinHandle childStdInWrite; - - // Now we can actually get around to starting a process - PROCESS_INFORMATION processInfo; - ZeroMemory(&processInfo, sizeof(processInfo)); - { - WinHandle childStdOutWrite; - WinHandle childStdErrWrite; - WinHandle childStdInRead; - - { - WinHandle childStdOutReadTmp; - WinHandle childStdErrReadTmp; - WinHandle childStdInWriteTmp; - // create stdout pipe for child process - SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdOutReadTmp.writeRef(), childStdOutWrite.writeRef(), &securityAttributes, 0)); - // create stderr pipe for child process - SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdErrReadTmp.writeRef(), childStdErrWrite.writeRef(), &securityAttributes, 0)); - // create stdin pipe for child process - SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdInRead.writeRef(), childStdInWriteTmp.writeRef(), &securityAttributes, 0)); - - HANDLE currentProcess = GetCurrentProcess(); - - // create a non-inheritable duplicate of the stdout reader - SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdOutReadTmp, currentProcess, childStdOutRead.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); - // create a non-inheritable duplicate of the stderr reader - SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdErrReadTmp, currentProcess, childStdErrRead.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); - // create a non-inheritable duplicate of the stdin writer - SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdInWriteTmp, currentProcess, childStdInWrite.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); - } - - - // TODO: switch to proper wide-character versions of these... - STARTUPINFOW startupInfo; - ZeroMemory(&startupInfo, sizeof(startupInfo)); - startupInfo.cb = sizeof(startupInfo); - startupInfo.hStdError = childStdErrWrite; - startupInfo.hStdOutput = childStdOutWrite; - startupInfo.hStdInput = childStdInRead; - startupInfo.dwFlags = STARTF_USESTDHANDLES; - - OSString pathBuffer; - LPCWSTR path = nullptr; - - if (commandLine.m_executableType == CommandLine::ExecutableType::Path) - { - StringBuilder cmd; - StringEscapeUtil::appendMaybeQuoted(getEscapeHandler(), commandLine.m_executable.getUnownedSlice(), cmd); - - pathBuffer = cmd.toWString(); - path = pathBuffer.begin(); - } - - // Produce the command line string - String cmdString = getCommandLineString(commandLine); - OSString cmdStringBuffer = cmdString.toWString(); - - // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa - // `CreateProcess` requires write access to this, for some reason... - BOOL success = CreateProcessW( - path, - (LPWSTR)cmdStringBuffer.begin(), - nullptr, - nullptr, - true, - CREATE_NO_WINDOW, - nullptr, // TODO: allow specifying environment variables? - nullptr, - &startupInfo, - &processInfo); - - if (!success) - { - DWORD err = GetLastError(); - SLANG_UNUSED(err); - - return SLANG_FAIL; - } - - // close handles we are now done with - CloseHandle(processInfo.hThread); - } - - // Create a thread to read from the child's stdout. - ThreadInfo stdOutThreadInfo; - stdOutThreadInfo.file = childStdOutRead; - WinHandle stdOutThread = CreateThread(nullptr, 0, &_readerThreadProc, (LPVOID)&stdOutThreadInfo, 0, nullptr); - - // Create a thread to read from the child's stderr. - ThreadInfo stdErrThreadInfo; - stdErrThreadInfo.file = childStdErrRead; - WinHandle stdErrThread = CreateThread(nullptr, 0, &_readerThreadProc, (LPVOID)&stdErrThreadInfo, 0, nullptr); - - // wait for the process to exit - // TODO: set a timeout as a safety measure... - WaitForSingleObject(processInfo.hProcess, INFINITE); - - // get exit code for process - // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess - - DWORD childExitCode = 0; - if (!GetExitCodeProcess(processInfo.hProcess, &childExitCode)) - { - // TODO(JS): Do we want to close here? It seems plausible because just because reading the exit code failed, doesn't mean the handle is closed - CloseHandle(processInfo.hProcess); - - return SLANG_FAIL; - } - - // wait for the reader threads - WaitForSingleObject(stdOutThread, INFINITE); - WaitForSingleObject(stdErrThread, INFINITE); - - CloseHandle(processInfo.hProcess); - - outExecuteResult.standardOutput = stdOutThreadInfo.output; - outExecuteResult.standardError = stdErrThreadInfo.output; - outExecuteResult.resultCode = childExitCode; - - return SLANG_OK; -} - -static uint64_t _getClockFrequency() -{ - LARGE_INTEGER timerFrequency; - QueryPerformanceFrequency(&timerFrequency); - return timerFrequency.QuadPart; -} - -static const uint64_t g_frequency = _getClockFrequency(); - -/* static */uint64_t ProcessUtil::getClockFrequency() -{ - return g_frequency; -} - -/* static */uint64_t ProcessUtil::getClockTick() -{ - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - return counter.QuadPart; -} - -} |
