Improve DLL loading and process launch handling

This commit is contained in:
Luke Street 2025-09-26 15:04:55 -06:00
parent 042a43ced1
commit b4ea1da959
8 changed files with 1096 additions and 216 deletions

View File

@ -7,34 +7,34 @@ namespace advapi32 {
return 1; // screw them for now
}
bool WIN_FUNC CryptReleaseContext(void* hProv, unsigned int dwFlags) {
BOOL WIN_FUNC CryptReleaseContext(void* hProv, unsigned int dwFlags) {
DEBUG_LOG("STUB: CryptReleaseContext %p %u\n", hProv, dwFlags);
return true;
return TRUE;
}
bool WIN_FUNC CryptAcquireContextW(void** phProv, const wchar_t* pszContainer, const wchar_t* pszProvider, unsigned int dwProvType, unsigned int dwFlags){
BOOL WIN_FUNC CryptAcquireContextW(void** phProv, const wchar_t* pszContainer, const wchar_t* pszProvider, unsigned int dwProvType, unsigned int dwFlags){
DEBUG_LOG("STUB: CryptAcquireContextW(%p)\n", phProv);
// to quote the guy above me: screw them for now
static int lmao = 42;
if (phProv) {
*phProv = &lmao;
return true;
return TRUE;
}
return false;
return FALSE;
}
bool WIN_FUNC CryptGenRandom(void* hProv, unsigned int dwLen, unsigned char* pbBuffer){
BOOL WIN_FUNC CryptGenRandom(void* hProv, unsigned int dwLen, unsigned char* pbBuffer){
DEBUG_LOG("STUB: CryptGenRandom(%p)\n", hProv);
if (!pbBuffer || dwLen == 0) return false;
if (!pbBuffer || dwLen == 0) return FALSE;
ssize_t ret = getrandom(pbBuffer, dwLen, 0);
if (ret < 0 || (size_t)ret != dwLen) {
return false;
return FALSE;
}
return true;
return TRUE;
}
}

View File

@ -12,6 +12,7 @@
#include <filesystem>
#include <fnmatch.h>
#include <string>
#include <strings.h>
#include "strutil.h"
#include <malloc.h>
#include <random>
@ -318,8 +319,8 @@ namespace kernel32 {
PROCESS_INFORMATION *lpProcessInformation
) {
DEBUG_LOG("CreateProcessA %s \"%s\" %p %p %d 0x%x %p %s %p %p\n",
lpApplicationName,
lpCommandLine,
lpApplicationName ? lpApplicationName : "<null>",
lpCommandLine ? lpCommandLine : "<null>",
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
@ -330,63 +331,46 @@ namespace kernel32 {
lpProcessInformation
);
// Argument parsing
// First: how many arguments do we have?
size_t argc = 2;
for (size_t i = 1; i < strlen(lpCommandLine); i++) {
if (isspace(lpCommandLine[i]) && !isspace(lpCommandLine[i - 1]))
argc++;
}
char **argv = (char **) calloc(argc + 1, sizeof(char*));
argv[0] = wibo::executableName;
std::string pathStr = files::pathFromWindows(lpApplicationName).string();
argv[1] = (char *) pathStr.c_str();
char* arg = strtok(lpCommandLine, " ");
size_t current_arg_index = 2;
while (arg != NULL) {
// We're deliberately discarding the first token here
// to prevent from doubling up on the target executable name
// (it appears as lpApplicationName, and as the first token in lpCommandLine)
arg = strtok(NULL, " ");
if (arg) {
// Trim all quotation marks from the start and the end of the string
while(*arg == '\"') {
arg++;
}
char* end = arg + strlen(arg) - 1;
while(end > arg && *end == '\"') {
*end = '\0';
end--;
}
}
argv[current_arg_index++] = arg;
}
argv[argc] = NULL; // Last element in argv should be a null pointer
// YET TODO: take into account process / thread attributes, environment variables
// working directory, etc.
setenv("WIBO_DEBUG_INDENT", std::to_string(wibo::debugIndent + 1).c_str(), true);
pid_t pid;
if (posix_spawn(&pid, wibo::executableName, NULL, NULL, argv, environ)) {
std::string application = lpApplicationName ? lpApplicationName : "";
std::vector<std::string> arguments = processes::splitCommandLine(lpCommandLine);
if (application.empty()) {
if (arguments.empty()) {
wibo::lastError = ERROR_FILE_NOT_FOUND;
return 0;
};
}
application = arguments.front();
}
if (arguments.empty()) {
arguments.push_back(application);
}
*lpProcessInformation = {
.hProcess = processes::allocProcessHandle(pid),
.hThread = nullptr,
.dwProcessId = (DWORD) pid,
.dwThreadId = 42
};
auto resolved = processes::resolveExecutable(application, true);
if (!resolved) {
wibo::lastError = ERROR_FILE_NOT_FOUND;
return 0;
}
pid_t pid = -1;
int spawnResult = processes::spawnViaWibo(*resolved, arguments, &pid);
if (spawnResult != 0) {
wibo::lastError = (spawnResult == ENOENT) ? ERROR_FILE_NOT_FOUND : ERROR_ACCESS_DENIED;
return 0;
}
if (lpProcessInformation) {
lpProcessInformation->hProcess = processes::allocProcessHandle(pid);
lpProcessInformation->hThread = nullptr;
lpProcessInformation->dwProcessId = static_cast<DWORD>(pid);
lpProcessInformation->dwThreadId = 0;
}
wibo::lastError = ERROR_SUCCESS;
(void)lpProcessAttributes;
(void)lpThreadAttributes;
(void)bInheritHandles;
(void)dwCreationFlags;
(void)lpEnvironment;
(void)lpCurrentDirectory;
(void)lpStartupInfo;
return 1;
}
@ -1918,6 +1902,15 @@ namespace kernel32 {
return wibo::loadModule(lpLibFileName);
}
HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName) {
DEBUG_LOG("LoadLibraryW\n");
if (!lpLibFileName) {
return nullptr;
}
auto filename = wideStringToString(lpLibFileName);
return LoadLibraryA(filename.c_str());
}
HMODULE WIN_FUNC LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) {
assert(!hFile);
DEBUG_LOG("LoadLibraryExW(%x) -> ", dwFlags);
@ -2554,35 +2547,74 @@ namespace kernel32 {
return 0; // fail
}
static std::string convertEnvValueForWindows(const std::string &name, const char *rawValue) {
if (!rawValue) {
return std::string();
}
if (strcasecmp(name.c_str(), "PATH") != 0) {
return rawValue;
}
std::string converted = files::hostPathListToWindows(rawValue);
return converted.empty() ? std::string(rawValue) : converted;
}
static std::string convertEnvValueToHost(const std::string &name, const char *rawValue) {
if (!rawValue) {
return std::string();
}
if (strcasecmp(name.c_str(), "PATH") != 0) {
return rawValue;
}
std::string converted = files::windowsPathListToHost(rawValue);
return converted.empty() ? std::string(rawValue) : converted;
}
DWORD WIN_FUNC GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize) {
DEBUG_LOG("GetEnvironmentVariableA: %s\n", lpName);
const char *value = getenv(lpName);
if (!value) {
if (!lpName) {
return 0;
}
unsigned int len = strlen(value);
const char *rawValue = getenv(lpName);
if (!rawValue) {
return 0;
}
std::string converted = convertEnvValueForWindows(lpName, rawValue);
const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted;
unsigned int len = finalValue.size();
if (nSize == 0) {
return len + 1;
}
if (nSize < len) {
if (nSize <= len) {
return len;
}
memcpy(lpBuffer, value, len + 1);
memcpy(lpBuffer, finalValue.c_str(), len + 1);
return len;
}
unsigned int WIN_FUNC SetEnvironmentVariableA(const char *lpName, const char *lpValue) {
DEBUG_LOG("SetEnvironmentVariableA: %s=%s\n", lpName, lpValue);
return setenv(lpName, lpValue, 1 /* OVERWRITE */);
DEBUG_LOG("SetEnvironmentVariableA: %s=%s\n", lpName, lpValue ? lpValue : "<null>");
if (!lpName) {
return 0;
}
if (!lpValue) {
return unsetenv(lpName);
}
std::string hostValue = convertEnvValueToHost(lpName, lpValue);
const char *valuePtr = hostValue.empty() ? lpValue : hostValue.c_str();
return setenv(lpName, valuePtr, 1 /* OVERWRITE */);
}
DWORD WIN_FUNC GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) {
DEBUG_LOG("GetEnvironmentVariableW: %s\n", wideStringToString(lpName).c_str());
const char *value = getenv(wideStringToString(lpName).c_str());
if (!value) {
std::string name = wideStringToString(lpName);
DEBUG_LOG("GetEnvironmentVariableW: %s\n", name.c_str());
const char *rawValue = getenv(name.c_str());
if (!rawValue) {
return 0;
}
auto wideValue = stringToWideString(value);
std::string converted = convertEnvValueForWindows(name, rawValue);
const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted;
auto wideValue = stringToWideString(finalValue.c_str());
const auto len = wideValue.size();
if (nSize < len) {
return len;
@ -2908,6 +2940,7 @@ static void *resolveByName(const char *name) {
if (strcmp(name, "LockResource") == 0) return (void *) kernel32::LockResource;
if (strcmp(name, "SizeofResource") == 0) return (void *) kernel32::SizeofResource;
if (strcmp(name, "LoadLibraryA") == 0) return (void *) kernel32::LoadLibraryA;
if (strcmp(name, "LoadLibraryW") == 0) return (void *) kernel32::LoadLibraryW;
if (strcmp(name, "LoadLibraryExW") == 0) return (void *) kernel32::LoadLibraryExW;
if (strcmp(name, "DisableThreadLibraryCalls") == 0) return (void *) kernel32::DisableThreadLibraryCalls;
if (strcmp(name, "FreeLibrary") == 0) return (void *) kernel32::FreeLibrary;

View File

@ -11,19 +11,27 @@
#include <cstring>
#include <cwchar>
#include <cwctype>
#include <filesystem>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <strings.h>
#include <type_traits>
#include <unistd.h>
#include <vector>
#include <spawn.h>
#include <sys/wait.h>
#include "files.h"
#include "processes.h"
#include "strutil.h"
typedef void (*_PVFV)();
typedef int (*_PIFV)();
using _onexit_t = _PIFV;
extern "C" char **environ;
namespace msvcrt {
int _commode;
int _fmode;
@ -66,6 +74,60 @@ namespace msvcrt {
return tables.back();
}
std::string normalizeEnvStringForWindows(const char *src) {
if (!src) {
return std::string();
}
std::string entry(src);
auto pos = entry.find('=');
if (pos == std::string::npos) {
return entry;
}
std::string name = entry.substr(0, pos);
std::string value = entry.substr(pos + 1);
if (strcasecmp(name.c_str(), "PATH") == 0) {
std::string converted = files::hostPathListToWindows(value);
std::string result = converted.empty() ? value : converted;
std::string exeDir;
if (wibo::argv && wibo::argv[0]) {
std::filesystem::path exePath = std::filesystem::absolute(std::filesystem::path(wibo::argv[0])).parent_path();
if (!exePath.empty()) {
exeDir = files::pathToWindows(exePath);
}
}
if (!exeDir.empty()) {
std::string loweredResult = result;
std::string loweredExe = exeDir;
std::transform(loweredResult.begin(), loweredResult.end(), loweredResult.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
std::transform(loweredExe.begin(), loweredExe.end(), loweredExe.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
bool present = false;
size_t start = 0;
while (start <= loweredResult.size()) {
size_t end = loweredResult.find(';', start);
if (end == std::string::npos) {
end = loweredResult.size();
}
if (loweredResult.substr(start, end - start) == loweredExe) {
present = true;
break;
}
if (end == loweredResult.size()) {
break;
}
start = end + 1;
}
if (!present) {
if (!result.empty() && result.back() != ';') {
result.push_back(';');
}
result += exeDir;
}
}
entry = name + "=" + result;
}
return entry;
}
template <typename CharT>
struct StringListStorage {
std::vector<std::unique_ptr<CharT[]>> strings;
@ -103,23 +165,19 @@ namespace msvcrt {
};
std::vector<char> copyNarrowString(const char *src) {
if (!src) {
src = "";
}
size_t len = std::strlen(src);
std::string normalized = normalizeEnvStringForWindows(src);
size_t len = normalized.size();
std::vector<char> result(len + 1);
if (len > 0) {
std::memcpy(result.data(), src, len);
std::memcpy(result.data(), normalized.data(), len);
}
result[len] = '\0';
return result;
}
std::vector<uint16_t> copyWideString(const char *src) {
if (!src) {
src = "";
}
return stringToWideString(src);
std::string normalized = normalizeEnvStringForWindows(src);
return stringToWideString(normalized.c_str());
}
template <typename CharT, typename Converter>
@ -427,6 +485,90 @@ namespace msvcrt {
std::free(ptr);
}
void* WIN_ENTRY memcpy(void *dest, const void *src, size_t count) {
return std::memcpy(dest, src, count);
}
void* WIN_ENTRY memmove(void *dest, const void *src, size_t count) {
return std::memmove(dest, src, count);
}
int WIN_ENTRY fflush(FILE *stream) {
return std::fflush(stream);
}
FILE *WIN_ENTRY fopen(const char *filename, const char *mode) {
return std::fopen(filename, mode);
}
int WIN_ENTRY _dup2(int fd1, int fd2) {
return dup2(fd1, fd2);
}
int WIN_ENTRY _isatty(int fd) {
return isatty(fd);
}
int WIN_ENTRY fseek(FILE *stream, long offset, int origin) {
return std::fseek(stream, offset, origin);
}
long WIN_ENTRY ftell(FILE *stream) {
return std::ftell(stream);
}
int WIN_ENTRY feof(FILE *stream) {
return std::feof(stream);
}
int WIN_ENTRY fputws(const uint16_t *str, FILE *stream) {
std::wstring temp;
if (str) {
for (const uint16_t *cursor = str; *cursor; ++cursor) {
temp.push_back(static_cast<wchar_t>(*cursor));
}
}
return std::fputws(temp.c_str(), stream);
}
uint16_t* WIN_ENTRY fgetws(uint16_t *buffer, int size, FILE *stream) {
if (!buffer || size <= 0) {
return nullptr;
}
std::vector<wchar_t> temp(static_cast<size_t>(size));
wchar_t *res = std::fgetws(temp.data(), size, stream);
if (!res) {
return nullptr;
}
for (int i = 0; i < size; ++i) {
buffer[i] = static_cast<uint16_t>(temp[i]);
if (temp[i] == L'\0') {
break;
}
}
return buffer;
}
wint_t WIN_ENTRY fgetwc(FILE *stream) {
return std::fgetwc(stream);
}
int WIN_ENTRY _wfopen_s(FILE **stream, const uint16_t *filename, const uint16_t *mode) {
if (!stream || !filename || !mode) {
errno = EINVAL;
return EINVAL;
}
std::string narrowName = wideStringToString(filename);
std::string narrowMode = wideStringToString(mode);
FILE *handle = std::fopen(narrowName.c_str(), narrowMode.c_str());
if (!handle) {
*stream = nullptr;
return errno ? errno : EINVAL;
}
*stream = handle;
return 0;
}
static uint16_t toLower(uint16_t ch) {
if (ch >= 'A' && ch <= 'Z') {
return static_cast<uint16_t>(ch + ('a' - 'A'));
@ -463,6 +605,234 @@ namespace msvcrt {
return static_cast<int>(a) - static_cast<int>(b);
}
int WIN_ENTRY _wmakepath_s(uint16_t *path, size_t sizeInWords, const uint16_t *drive, const uint16_t *dir,
const uint16_t *fname, const uint16_t *ext) {
if (!path || sizeInWords == 0) {
return EINVAL;
}
path[0] = 0;
std::u16string result;
auto append = [&](const uint16_t *src) {
if (!src || !*src) {
return;
}
for (const uint16_t *cursor = src; *cursor; ++cursor) {
result.push_back(static_cast<char16_t>(*cursor));
}
};
if (drive && *drive) {
result.push_back(static_cast<char16_t>(drive[0]));
if (drive[1] == u':') {
result.push_back(u':');
append(drive + 2);
} else {
result.push_back(u':');
append(drive + 1);
}
}
auto appendDir = [&](const uint16_t *directory) {
if (!directory || !*directory) {
return;
}
append(directory);
if (result.empty()) {
return;
}
char16_t last = result.back();
if (last != u'/' && last != u'\\') {
result.push_back(u'\\');
}
};
appendDir(dir);
append(fname);
if (ext && *ext) {
if (*ext != u'.') {
result.push_back(u'.');
append(ext);
} else {
append(ext);
}
}
size_t required = result.size() + 1;
if (required > sizeInWords) {
path[0] = 0;
return ERANGE;
}
for (size_t i = 0; i < result.size(); ++i) {
path[i] = static_cast<uint16_t>(result[i]);
}
path[result.size()] = 0;
return 0;
}
int WIN_ENTRY _wputenv_s(const uint16_t *varname, const uint16_t *value) {
if (!varname || !value) {
errno = EINVAL;
return EINVAL;
}
if (!*varname) {
errno = EINVAL;
return EINVAL;
}
for (const uint16_t *cursor = varname; *cursor; ++cursor) {
if (*cursor == static_cast<uint16_t>('=')) {
errno = EINVAL;
return EINVAL;
}
}
std::string name = wideStringToString(varname);
if (name.empty()) {
errno = EINVAL;
return EINVAL;
}
int resultCode = 0;
if (!*value) {
if (unsetenv(name.c_str()) != 0) {
resultCode = errno != 0 ? errno : EINVAL;
}
} else {
std::string narrowValue = wideStringToString(value);
if (setenv(name.c_str(), narrowValue.c_str(), 1) != 0) {
resultCode = errno != 0 ? errno : EINVAL;
}
}
if (resultCode != 0) {
errno = resultCode;
return resultCode;
}
getMainArgsCommon<char>(nullptr, nullptr, nullptr, copyNarrowString);
getMainArgsCommon<uint16_t>(nullptr, nullptr, nullptr, copyWideString);
return 0;
}
unsigned long WIN_ENTRY wcsspn(const uint16_t *str1, const uint16_t *str2) {
if (!str1 || !str2) {
return 0;
}
unsigned long count = 0;
for (const uint16_t *p = str1; *p; ++p) {
bool match = false;
for (const uint16_t *q = str2; *q; ++q) {
if (*p == *q) {
match = true;
break;
}
}
if (!match) {
break;
}
++count;
}
return count;
}
long WIN_ENTRY _wtol(const uint16_t *str) {
return wstrtol(str, nullptr, 10);
}
int WIN_ENTRY _wcsupr_s(uint16_t *str, size_t size) {
if (!str || size == 0) {
return EINVAL;
}
size_t len = wstrnlen(str, size);
if (len >= size) {
return ERANGE;
}
for (size_t i = 0; i < len; ++i) {
wchar_t ch = static_cast<wchar_t>(str[i]);
str[i] = static_cast<uint16_t>(std::towupper(ch));
}
return 0;
}
int WIN_ENTRY _wcslwr_s(uint16_t *str, size_t size) {
if (!str || size == 0) {
return EINVAL;
}
size_t len = wstrnlen(str, size);
if (len >= size) {
return ERANGE;
}
for (size_t i = 0; i < len; ++i) {
wchar_t ch = static_cast<wchar_t>(str[i]);
str[i] = static_cast<uint16_t>(std::towlower(ch));
}
return 0;
}
wint_t WIN_ENTRY towlower(wint_t ch) {
return static_cast<wint_t>(std::towlower(static_cast<wchar_t>(ch)));
}
int WIN_ENTRY _ftime64_s(void *timeb) {
DEBUG_LOG("STUB: _ftime64_s(%p)\n", timeb);
return 0;
}
int WIN_ENTRY _crt_debugger_hook(int value) {
DEBUG_LOG("_crt_debugger_hook(%d)\n", value);
(void)value;
return 0;
}
int WIN_ENTRY _configthreadlocale(int mode) {
static int currentMode = 0;
int previous = currentMode;
if (mode == -1) {
return previous;
}
if (mode == 0 || mode == 1 || mode == 2) {
currentMode = mode;
return previous;
}
errno = EINVAL;
return -1;
}
static void abort_and_log(const char *reason) {
DEBUG_LOG("Runtime abort: %s\n", reason ? reason : "");
std::abort();
}
int WIN_ENTRY _amsg_exit(int reason) {
DEBUG_LOG("_amsg_exit(%d)\n", reason);
abort_and_log("_amsg_exit");
return reason;
}
void WIN_ENTRY _invoke_watson(const uint16_t *, const uint16_t *, const uint16_t *, unsigned int, uintptr_t) {
DEBUG_LOG("_invoke_watson\n");
abort_and_log("_invoke_watson");
}
void WIN_ENTRY terminateShim() {
abort_and_log("terminate");
}
int WIN_ENTRY _except_handler4_common(void *, void *, void *, void *) {
DEBUG_LOG("_except_handler4_common\n");
return 0;
}
long WIN_ENTRY _XcptFilter(unsigned long code, void *) {
DEBUG_LOG("_XcptFilter(%lu)\n", code);
return 0;
}
int WIN_ENTRY _get_wpgmptr(uint16_t** pValue){
DEBUG_LOG("_get_wpgmptr(%p)\n", pValue);
if(!pValue) return 22;
@ -576,9 +946,17 @@ namespace msvcrt {
}
int WIN_ENTRY _waccess_s(const uint16_t* path, int mode){
std::string str = wideStringToString(path);
DEBUG_LOG("_waccess_s %s\n", str.c_str());
return access(str.c_str(), mode);
std::string original = wideStringToString(path);
DEBUG_LOG("_waccess_s %s\n", original.c_str());
std::filesystem::path host = files::pathFromWindows(original.c_str());
std::string candidate;
if (!host.empty()) {
candidate = host.string();
} else {
candidate = original;
std::replace(candidate.begin(), candidate.end(), '\\', '/');
}
return access(candidate.c_str(), mode);
}
void* WIN_ENTRY memset(void *s, int c, size_t n){
@ -744,29 +1122,16 @@ namespace msvcrt {
return wstrtoul(strSource, endptr, base);
}
int WIN_ENTRY _dup2(int fd1, int fd2){
return dup2(fd1, fd2);
}
FILE* WIN_ENTRY _wfsopen(const uint16_t* filename, const uint16_t* mode, int shflag){
if (!filename || !mode) return nullptr;
std::string fname_str = wideStringToString(filename);
std::string mode_str = wideStringToString(mode);
DEBUG_LOG("_wfsopen file %s, mode %s\n", fname_str.c_str(), mode_str.c_str());
(void)shflag;
return fopen(fname_str.c_str(), mode_str.c_str());
}
int WIN_ENTRY fputws(const uint16_t* str, FILE* stream){
if(!str || !stream) return EOF;
std::string fname_str = wideStringToString(str);
DEBUG_LOG("fputws %s\n", fname_str.c_str());
if(fputs(fname_str.c_str(), stream) < 0) return EOF;
else return 0;
}
int WIN_ENTRY puts(const char *str) {
if (!str) {
str = "(null)";
@ -787,9 +1152,9 @@ namespace msvcrt {
DEBUG_LOG("flushall\n");
int count = 0;
if (fflush(stdin) == 0) count++;
if (fflush(stdout) == 0) count++;
if (fflush(stderr) == 0) count++;
if (msvcrt::fflush(stdin) == 0) count++;
if (msvcrt::fflush(stdout) == 0) count++;
if (msvcrt::fflush(stderr) == 0) count++;
return count;
}
@ -799,11 +1164,63 @@ namespace msvcrt {
}
intptr_t WIN_ENTRY _wspawnvp(int mode, const uint16_t* cmdname, const uint16_t* const * argv) {
std::string str_cmd = wideStringToString(cmdname);
DEBUG_LOG("STUB: _wspawnvp %s\n", str_cmd.c_str());
if (!cmdname || !argv) {
errno = EINVAL;
return -1;
}
std::string command = wideStringToString(cmdname);
DEBUG_LOG("_wspawnvp(mode=%d, cmd=%s)\n", mode, command.c_str());
std::vector<std::string> argStorage;
for (const uint16_t *const *cursor = argv; *cursor; ++cursor) {
argStorage.emplace_back(wideStringToString(*cursor));
}
if (argStorage.empty()) {
argStorage.emplace_back(command);
}
auto resolved = processes::resolveExecutable(command, true);
if (!resolved) {
errno = ENOENT;
DEBUG_LOG("\tfailed to resolve executable for %s\n", command.c_str());
return -1;
}
pid_t pid = -1;
int spawnResult = processes::spawnViaWibo(*resolved, argStorage, &pid);
if (spawnResult != 0) {
errno = spawnResult;
DEBUG_LOG("\tspawnViaWibo failed: %d\n", spawnResult);
return -1;
}
constexpr int P_WAIT = 0;
constexpr int P_DETACH = 2;
if (mode == P_WAIT) {
int status = 0;
if (waitpid(pid, &status, 0) == -1) {
DEBUG_LOG("\twaitpid failed: %d\n", errno);
return -1;
}
if (WIFEXITED(status)) {
return static_cast<intptr_t>(WEXITSTATUS(status));
}
if (WIFSIGNALED(status)) {
errno = EINTR;
}
return -1;
}
if (mode == P_DETACH) {
return 0;
}
// _P_NOWAIT and unknown flags: return process id
return static_cast<intptr_t>(pid);
}
int WIN_ENTRY _wunlink(const uint16_t *filename){
std::string str = wideStringToString(filename);
DEBUG_LOG("_wunlink %s\n", str.c_str());
@ -889,11 +1306,39 @@ static void *resolveByName(const char *name) {
if (strcmp(name, "__dllonexit") == 0) return (void*)msvcrt::__dllonexit;
if (strcmp(name, "free") == 0) return (void*)msvcrt::free;
if (strcmp(name, "_wcsicmp") == 0) return (void*)msvcrt::_wcsicmp;
if (strcmp(name, "_wmakepath_s") == 0) return (void*)msvcrt::_wmakepath_s;
if (strcmp(name, "_wputenv_s") == 0) return (void*)msvcrt::_wputenv_s;
if (strcmp(name, "_get_wpgmptr") == 0) return (void*)msvcrt::_get_wpgmptr;
if (strcmp(name, "_wsplitpath_s") == 0) return (void*)msvcrt::_wsplitpath_s;
if (strcmp(name, "wcscat_s") == 0) return (void*)msvcrt::wcscat_s;
if (strcmp(name, "_wcsdup") == 0) return (void*)msvcrt::_wcsdup;
if (strcmp(name, "memset") == 0) return (void*)msvcrt::memset;
if (strcmp(name, "memcpy") == 0) return (void*)msvcrt::memcpy;
if (strcmp(name, "memmove") == 0) return (void*)msvcrt::memmove;
if (strcmp(name, "fflush") == 0) return (void*)msvcrt::fflush;
if (strcmp(name, "fopen") == 0) return (void*)msvcrt::fopen;
if (strcmp(name, "fseek") == 0) return (void*)msvcrt::fseek;
if (strcmp(name, "ftell") == 0) return (void*)msvcrt::ftell;
if (strcmp(name, "feof") == 0) return (void*)msvcrt::feof;
if (strcmp(name, "fgetws") == 0) return (void*)msvcrt::fgetws;
if (strcmp(name, "fgetwc") == 0) return (void*)msvcrt::fgetwc;
if (strcmp(name, "fputws") == 0) return (void*)msvcrt::fputws;
if (strcmp(name, "_wfopen_s") == 0) return (void*)msvcrt::_wfopen_s;
if (strcmp(name, "wcsspn") == 0) return (void*)msvcrt::wcsspn;
if (strcmp(name, "_wtol") == 0) return (void*)msvcrt::_wtol;
if (strcmp(name, "_wcsupr_s") == 0) return (void*)msvcrt::_wcsupr_s;
if (strcmp(name, "_wcslwr_s") == 0) return (void*)msvcrt::_wcslwr_s;
if (strcmp(name, "_dup2") == 0) return (void*)msvcrt::_dup2;
if (strcmp(name, "_isatty") == 0) return (void*)msvcrt::_isatty;
if (strcmp(name, "towlower") == 0) return (void*)msvcrt::towlower;
if (strcmp(name, "_ftime64_s") == 0) return (void*)msvcrt::_ftime64_s;
if (strcmp(name, "_crt_debugger_hook") == 0) return (void*)msvcrt::_crt_debugger_hook;
if (strcmp(name, "_configthreadlocale") == 0) return (void*)msvcrt::_configthreadlocale;
if (strcmp(name, "_amsg_exit") == 0) return (void*)msvcrt::_amsg_exit;
if (strcmp(name, "_invoke_watson") == 0) return (void*)msvcrt::_invoke_watson;
if (strcmp(name, "_except_handler4_common") == 0) return (void*)msvcrt::_except_handler4_common;
if (strcmp(name, "_XcptFilter") == 0) return (void*)msvcrt::_XcptFilter;
if (strcmp(name, "?terminate@@YAXXZ") == 0) return (void*)msvcrt::terminateShim;
if (strcmp(name, "wcsncpy_s") == 0) return (void*)msvcrt::wcsncpy_s;
if (strcmp(name, "wcsncat_s") == 0) return (void*)msvcrt::wcsncat_s;
if (strcmp(name, "_itow_s") == 0) return (void*)msvcrt::_itow_s;

120
files.cpp
View File

@ -2,10 +2,58 @@
#include "files.h"
#include "handles.h"
#include <algorithm>
#include <cctype>
#include <map>
#include <optional>
#include <strings.h>
#include <string>
namespace files {
static std::vector<std::string> splitList(const std::string &value, char delimiter) {
std::vector<std::string> entries;
size_t start = 0;
while (start <= value.size()) {
size_t end = value.find(delimiter, start);
if (end == std::string::npos) {
end = value.size();
}
entries.emplace_back(value.substr(start, end - start));
if (end == value.size()) {
break;
}
start = end + 1;
}
return entries;
}
static std::string toWindowsPathEntry(const std::string &entry) {
if (entry.empty()) {
return std::string();
}
bool looksWindows = entry.find('\\') != std::string::npos ||
(entry.size() >= 2 && entry[1] == ':' && entry[0] != '/');
if (looksWindows) {
std::string normalized = entry;
std::replace(normalized.begin(), normalized.end(), '/', '\\');
return normalized;
}
return pathToWindows(std::filesystem::path(entry));
}
static std::string toHostPathEntry(const std::string &entry) {
if (entry.empty()) {
return std::string();
}
auto converted = pathFromWindows(entry.c_str());
if (!converted.empty()) {
return converted.string();
}
std::string normalized = entry;
std::replace(normalized.begin(), normalized.end(), '\\', '/');
return normalized;
}
static void *stdinHandle;
static void *stdoutHandle;
static void *stderrHandle;
@ -123,4 +171,76 @@ namespace files {
stdoutHandle = allocFpHandle(stdout);
stderrHandle = allocFpHandle(stderr);
}
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
const std::string &filename) {
std::error_code ec;
if (directory.empty()) {
return std::nullopt;
}
if (!std::filesystem::exists(directory, ec) || !std::filesystem::is_directory(directory, ec)) {
return std::nullopt;
}
std::string needle = filename;
std::transform(needle.begin(), needle.end(), needle.begin(), [](unsigned char ch) { return std::tolower(ch); });
for (const auto &entry : std::filesystem::directory_iterator(directory, ec)) {
if (ec) {
break;
}
std::string candidate = entry.path().filename().string();
std::transform(candidate.begin(), candidate.end(), candidate.begin(), [](unsigned char ch) { return std::tolower(ch); });
if (candidate == needle) {
return canonicalPath(entry.path());
}
}
auto direct = directory / filename;
if (std::filesystem::exists(direct, ec)) {
return canonicalPath(direct);
}
return std::nullopt;
}
std::filesystem::path canonicalPath(const std::filesystem::path &path) {
std::error_code ec;
auto canonical = std::filesystem::weakly_canonical(path, ec);
if (!ec) {
return canonical;
}
return std::filesystem::absolute(path);
}
std::string hostPathListToWindows(const std::string &value) {
if (value.empty()) {
return value;
}
char delimiter = value.find(';') != std::string::npos ? ';' : ':';
auto entries = splitList(value, delimiter);
std::string result;
for (size_t i = 0; i < entries.size(); ++i) {
if (i != 0) {
result.push_back(';');
}
if (!entries[i].empty()) {
result += toWindowsPathEntry(entries[i]);
}
}
return result;
}
std::string windowsPathListToHost(const std::string &value) {
if (value.empty()) {
return value;
}
auto entries = splitList(value, ';');
std::string result;
for (size_t i = 0; i < entries.size(); ++i) {
if (i != 0) {
result.push_back(':');
}
if (!entries[i].empty()) {
result += toHostPathEntry(entries[i]);
}
}
return result;
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <filesystem>
#include <optional>
#include <string>
namespace files {
@ -11,6 +12,10 @@ namespace files {
void *getStdHandle(uint32_t nStdHandle);
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle);
void init();
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory, const std::string &filename);
std::filesystem::path canonicalPath(const std::filesystem::path &path);
std::string hostPathListToWindows(const std::string &value);
std::string windowsPathListToHost(const std::string &value);
}
inline bool endsWith(const std::string &str, const std::string &suffix) {

View File

@ -110,6 +110,8 @@ struct ModuleRegistry {
std::optional<std::filesystem::path> dllDirectory;
bool initialized = false;
std::unordered_map<void *, wibo::ModuleInfo *> onExitTables;
std::unordered_map<const wibo::Module *, std::vector<std::string>> builtinAliasLists;
std::unordered_map<std::string, wibo::ModuleInfo *> builtinAliasMap;
};
ModuleRegistry &registry() {
@ -180,32 +182,6 @@ std::string normalizedBaseKey(const ParsedModuleName &parsed) {
return normalizeAlias(base);
}
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
const std::string &filename) {
std::error_code ec;
if (directory.empty()) {
return std::nullopt;
}
if (!std::filesystem::exists(directory, ec) || !std::filesystem::is_directory(directory, ec)) {
return std::nullopt;
}
const std::string lower = toLowerCopy(filename);
for (const auto &entry : std::filesystem::directory_iterator(directory, ec)) {
if (ec) {
break;
}
const std::string candidate = toLowerCopy(entry.path().filename().string());
if (candidate == lower) {
return std::filesystem::canonical(entry.path(), ec);
}
}
auto direct = directory / filename;
if (std::filesystem::exists(direct, ec)) {
return std::filesystem::canonical(direct, ec);
}
return std::nullopt;
}
std::optional<std::filesystem::path> combineAndFind(const std::filesystem::path &directory,
const std::string &filename) {
if (filename.empty()) {
@ -214,16 +190,7 @@ std::optional<std::filesystem::path> combineAndFind(const std::filesystem::path
if (directory.empty()) {
return std::nullopt;
}
return findCaseInsensitiveFile(directory, filename);
}
std::filesystem::path canonicalPath(const std::filesystem::path &path) {
std::error_code ec;
auto canonical = std::filesystem::weakly_canonical(path, ec);
if (!ec) {
return canonical;
}
return std::filesystem::absolute(path);
return files::findCaseInsensitiveFile(directory, filename);
}
std::vector<std::filesystem::path> collectSearchDirectories(bool alteredSearchPath) {
@ -306,10 +273,10 @@ std::optional<std::filesystem::path> resolveModuleOnDisk(const std::string &requ
auto combined = parsed.directory + "\\" + candidate;
auto posixPath = files::pathFromWindows(combined.c_str());
if (!posixPath.empty()) {
auto resolved = findCaseInsensitiveFile(std::filesystem::path(posixPath).parent_path(),
auto resolved = files::findCaseInsensitiveFile(std::filesystem::path(posixPath).parent_path(),
std::filesystem::path(posixPath).filename().string());
if (resolved) {
return canonicalPath(*resolved);
return files::canonicalPath(*resolved);
}
}
}
@ -321,7 +288,7 @@ std::optional<std::filesystem::path> resolveModuleOnDisk(const std::string &requ
for (const auto &candidate : names) {
auto resolved = combineAndFind(dir, candidate);
if (resolved) {
return canonicalPath(*resolved);
return files::canonicalPath(*resolved);
}
}
}
@ -330,7 +297,7 @@ std::optional<std::filesystem::path> resolveModuleOnDisk(const std::string &requ
}
std::string storageKeyForPath(const std::filesystem::path &path) {
return normalizeAlias(files::pathToWindows(canonicalPath(path)));
return normalizeAlias(files::pathToWindows(files::canonicalPath(path)));
}
std::string storageKeyForBuiltin(const std::string &normalizedName) { return normalizedName; }
@ -349,7 +316,13 @@ void registerAlias(const std::string &alias, wibo::ModuleInfo *info) {
return;
}
auto &reg = registry();
if (reg.modulesByAlias.find(alias) == reg.modulesByAlias.end()) {
auto it = reg.modulesByAlias.find(alias);
if (it == reg.modulesByAlias.end()) {
reg.modulesByAlias[alias] = info;
return;
}
// Prefer externally loaded modules over built-ins when both are present.
if (it->second && it->second->module != nullptr && info->module == nullptr) {
reg.modulesByAlias[alias] = info;
}
}
@ -369,10 +342,20 @@ void registerBuiltinModule(const wibo::Module *module) {
auto &reg = registry();
reg.modulesByKey[storageKey] = std::move(entry);
reg.builtinAliasLists[module] = {};
auto &aliasList = reg.builtinAliasLists[module];
for (size_t i = 0; module->names[i]; ++i) {
registerAlias(normalizeAlias(module->names[i]), raw);
std::string alias = normalizeAlias(module->names[i]);
aliasList.push_back(alias);
registerAlias(alias, raw);
reg.builtinAliasMap[alias] = raw;
ParsedModuleName parsed = parseModuleName(module->names[i]);
registerAlias(normalizedBaseKey(parsed), raw);
std::string baseAlias = normalizedBaseKey(parsed);
if (baseAlias != alias) {
aliasList.push_back(baseAlias);
registerAlias(baseAlias, raw);
reg.builtinAliasMap[baseAlias] = raw;
}
}
}
@ -543,7 +526,7 @@ void shutdownModuleRegistry() {
ModuleInfo *moduleInfoFromHandle(HMODULE module) { return static_cast<ModuleInfo *>(module); }
void setDllDirectoryOverride(const std::filesystem::path &path) {
auto canonical = canonicalPath(path);
auto canonical = files::canonicalPath(path);
std::lock_guard<std::recursive_mutex> lock(registry().mutex);
registry().dllDirectory = canonical;
}
@ -645,50 +628,34 @@ HMODULE loadModule(const char *dllName) {
ensureInitialized();
ParsedModuleName parsed = parseModuleName(requested);
std::string alias = normalizedBaseKey(parsed);
ModuleInfo *existing = findByAlias(alias);
if (!existing) {
existing = findByAlias(normalizeAlias(requested));
}
if (existing) {
DEBUG_LOG(" found existing module alias %s\n", alias.c_str());
if (existing->refCount != UINT_MAX) {
existing->refCount++;
}
lastError = ERROR_SUCCESS;
return existing;
}
auto resolvedPath = resolveModuleOnDisk(requested, false);
if (!resolvedPath) {
DEBUG_LOG(" module not found on disk\n");
lastError = ERROR_MOD_NOT_FOUND;
return nullptr;
}
std::string key = storageKeyForPath(*resolvedPath);
auto &reg = registry();
auto it = reg.modulesByKey.find(key);
if (it != reg.modulesByKey.end()) {
ModuleInfo *info = it->second.get();
DWORD diskError = ERROR_SUCCESS;
auto tryLoadExternal = [&](const std::filesystem::path &path) -> ModuleInfo * {
std::string key = storageKeyForPath(path);
auto existingIt = reg.modulesByKey.find(key);
if (existingIt != reg.modulesByKey.end()) {
ModuleInfo *info = existingIt->second.get();
if (info->refCount != UINT_MAX) {
info->refCount++;
registerExternalModuleAliases(requested, *resolvedPath, info);
lastError = ERROR_SUCCESS;
}
registerExternalModuleAliases(requested, files::canonicalPath(path), info);
return info;
}
FILE *file = fopen(resolvedPath->c_str(), "rb");
FILE *file = fopen(path.c_str(), "rb");
if (!file) {
perror("loadModule");
lastError = ERROR_MOD_NOT_FOUND;
diskError = ERROR_MOD_NOT_FOUND;
return nullptr;
}
auto executable = std::make_unique<Executable>();
if (!executable->loadPE(file, true)) {
DEBUG_LOG(" loadPE failed for %s\n", resolvedPath->c_str());
DEBUG_LOG(" loadPE failed for %s\n", path.c_str());
fclose(file);
lastError = ERROR_BAD_EXE_FORMAT;
diskError = ERROR_BAD_EXE_FORMAT;
return nullptr;
}
fclose(file);
@ -696,8 +663,8 @@ HMODULE loadModule(const char *dllName) {
ModulePtr info = std::make_unique<ModuleInfo>();
info->module = nullptr;
info->originalName = requested;
info->normalizedName = alias;
info->resolvedPath = *resolvedPath;
info->normalizedName = normalizedBaseKey(parsed);
info->resolvedPath = files::canonicalPath(path);
info->executable = std::move(executable);
info->entryPoint = info->executable->entryPoint;
info->imageBase = info->executable->imageBuffer;
@ -708,13 +675,72 @@ HMODULE loadModule(const char *dllName) {
ModuleInfo *raw = info.get();
reg.modulesByKey[key] = std::move(info);
registerExternalModuleAliases(requested, *resolvedPath, raw);
registerExternalModuleAliases(requested, raw->resolvedPath, raw);
ensureExportsInitialized(*raw);
callDllMain(*raw, DLL_PROCESS_ATTACH);
lastError = ERROR_SUCCESS;
return raw;
};
auto resolveAndLoadExternal = [&]() -> ModuleInfo * {
auto resolvedPath = resolveModuleOnDisk(requested, false);
if (!resolvedPath) {
DEBUG_LOG(" module not found on disk\n");
return nullptr;
}
return tryLoadExternal(*resolvedPath);
};
std::string alias = normalizedBaseKey(parsed);
ModuleInfo *existing = findByAlias(alias);
if (!existing) {
existing = findByAlias(normalizeAlias(requested));
}
if (existing) {
DEBUG_LOG(" found existing module alias %s (builtin=%d)\n", alias.c_str(), existing->module != nullptr);
if (existing->module == nullptr) {
if (existing->refCount != UINT_MAX) {
existing->refCount++;
}
DEBUG_LOG(" returning existing external module %s\n", existing->originalName.c_str());
lastError = ERROR_SUCCESS;
return existing;
}
if (ModuleInfo *external = resolveAndLoadExternal()) {
DEBUG_LOG(" replaced builtin module %s with external copy\n", requested.c_str());
lastError = ERROR_SUCCESS;
return external;
}
lastError = ERROR_SUCCESS;
DEBUG_LOG(" returning builtin module %s\n", existing->originalName.c_str());
return existing;
}
if (ModuleInfo *external = resolveAndLoadExternal()) {
DEBUG_LOG(" loaded external module %s\n", requested.c_str());
lastError = ERROR_SUCCESS;
return external;
}
auto fallbackAlias = normalizedBaseKey(parsed);
ModuleInfo *builtin = nullptr;
auto builtinIt = reg.builtinAliasMap.find(fallbackAlias);
if (builtinIt != reg.builtinAliasMap.end()) {
builtin = builtinIt->second;
}
if (!builtin) {
builtinIt = reg.builtinAliasMap.find(normalizeAlias(requested));
if (builtinIt != reg.builtinAliasMap.end()) {
builtin = builtinIt->second;
}
}
if (builtin && builtin->module != nullptr) {
DEBUG_LOG(" falling back to builtin module %s\n", builtin->originalName.c_str());
lastError = (diskError != ERROR_SUCCESS) ? diskError : ERROR_SUCCESS;
return builtin;
}
lastError = (diskError != ERROR_SUCCESS) ? diskError : ERROR_MOD_NOT_FOUND;
return nullptr;
}
void freeModule(HMODULE module) {

View File

@ -1,7 +1,21 @@
#include "processes.h"
#include "common.h"
#include "files.h"
#include "handles.h"
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <optional>
#include <spawn.h>
#include <strings.h>
#include <string>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
extern "C" char **environ;
namespace processes {
void *allocProcessHandle(pid_t pid) {
@ -21,4 +35,233 @@ namespace processes {
assert(0);
}
}
static bool hasDirectoryComponent(const std::string &command) {
return command.find('/') != std::string::npos || command.find('\\') != std::string::npos ||
command.find(':') != std::string::npos;
}
static bool hasExtension(const std::string &command) {
auto pos = command.find_last_of('.');
auto slash = command.find_last_of("/\\");
return pos != std::string::npos && (slash == std::string::npos || pos > slash + 1);
}
static std::vector<std::string> pathextValues() {
const char *envValue = std::getenv("PATHEXT");
std::string raw = envValue ? envValue : ".COM;.EXE;.BAT;.CMD";
std::vector<std::string> exts;
size_t start = 0;
while (start <= raw.size()) {
size_t end = raw.find(';', start);
if (end == std::string::npos) {
end = raw.size();
}
std::string part = raw.substr(start, end - start);
if (!part.empty()) {
if (part[0] != '.') {
part.insert(part.begin(), '.');
}
exts.push_back(part);
}
if (end == raw.size()) {
break;
}
start = end + 1;
}
if (exts.empty()) {
exts = {".COM", ".EXE", ".BAT", ".CMD"};
}
return exts;
}
static std::vector<std::filesystem::path> parseHostPath(const std::string &value) {
std::vector<std::filesystem::path> paths;
const char *delims = strchr(value.c_str(), ';') ? ";" : ":";
size_t start = 0;
while (start <= value.size()) {
size_t end = value.find_first_of(delims, start);
if (end == std::string::npos) {
end = value.size();
}
std::string entry = value.substr(start, end - start);
if (!entry.empty()) {
bool looksWindows = entry.find('\\') != std::string::npos ||
(entry.size() >= 2 && entry[1] == ':' && entry[0] != '/');
std::filesystem::path candidate;
if (looksWindows) {
auto converted = files::pathFromWindows(entry.c_str());
if (!converted.empty()) {
candidate = converted;
}
}
if (candidate.empty()) {
candidate = std::filesystem::path(entry);
}
paths.push_back(std::move(candidate));
}
if (end == value.size()) {
break;
}
start = end + 1;
}
return paths;
}
static std::vector<std::filesystem::path> buildSearchDirectories() {
std::vector<std::filesystem::path> dirs;
dirs.push_back(std::filesystem::current_path());
if (const char *envPath = std::getenv("PATH")) {
auto parsed = parseHostPath(envPath);
dirs.insert(dirs.end(), parsed.begin(), parsed.end());
}
return dirs;
}
std::optional<std::filesystem::path> resolveExecutable(const std::string &command, bool searchPath) {
if (command.empty()) {
return std::nullopt;
}
std::vector<std::string> candidates;
candidates.push_back(command);
if (!hasExtension(command)) {
for (const auto &ext : pathextValues()) {
candidates.push_back(command + ext);
}
}
auto tryResolveDirect = [&](const std::string &name) -> std::optional<std::filesystem::path> {
auto host = files::pathFromWindows(name.c_str());
if (host.empty()) {
std::string normalized = name;
std::replace(normalized.begin(), normalized.end(), '\\', '/');
host = std::filesystem::path(normalized);
}
std::filesystem::path parent = host.parent_path().empty() ? std::filesystem::current_path() : host.parent_path();
std::string filename = host.filename().string();
auto resolved = files::findCaseInsensitiveFile(parent, filename);
if (resolved) {
return files::canonicalPath(*resolved);
}
std::error_code ec;
if (!filename.empty() && std::filesystem::exists(host, ec)) {
return files::canonicalPath(host);
}
return std::nullopt;
};
if (hasDirectoryComponent(command)) {
for (const auto &name : candidates) {
auto resolved = tryResolveDirect(name);
if (resolved) {
return resolved;
}
}
return std::nullopt;
}
if (searchPath) {
auto dirs = buildSearchDirectories();
for (const auto &dir : dirs) {
for (const auto &name : candidates) {
auto resolved = files::findCaseInsensitiveFile(dir, name);
if (resolved) {
return files::canonicalPath(*resolved);
}
}
}
}
return std::nullopt;
}
int spawnViaWibo(const std::filesystem::path &hostExecutable, const std::vector<std::string> &arguments, pid_t *pidOut) {
if (hostExecutable.empty()) {
return ENOENT;
}
std::vector<std::string> storage;
storage.reserve(arguments.size() + 1);
storage.push_back(hostExecutable.string());
for (const auto &arg : arguments) {
storage.push_back(arg);
}
std::vector<char *> nativeArgs;
nativeArgs.reserve(storage.size() + 2);
nativeArgs.push_back(wibo::executableName);
for (auto &entry : storage) {
nativeArgs.push_back(entry.data());
}
nativeArgs.push_back(nullptr);
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_init(&actions);
std::string indent = std::to_string(wibo::debugIndent + 1);
setenv("WIBO_DEBUG_INDENT", indent.c_str(), 1);
pid_t pid = -1;
int spawnResult = posix_spawn(&pid, wibo::executableName, &actions, nullptr, nativeArgs.data(), environ);
posix_spawn_file_actions_destroy(&actions);
if (spawnResult != 0) {
return spawnResult;
}
if (pidOut) {
*pidOut = pid;
}
return 0;
}
std::vector<std::string> splitCommandLine(const char *commandLine) {
std::vector<std::string> result;
if (!commandLine) {
return result;
}
std::string input(commandLine);
size_t i = 0;
size_t len = input.size();
while (i < len) {
while (i < len && (input[i] == ' ' || input[i] == '\t')) {
++i;
}
if (i >= len) {
break;
}
std::string arg;
bool inQuotes = false;
int backslashes = 0;
for (; i < len; ++i) {
char c = input[i];
if (c == '\\') {
++backslashes;
continue;
}
if (c == '"') {
if ((backslashes % 2) == 0) {
arg.append(backslashes / 2, '\\');
inQuotes = !inQuotes;
} else {
arg.append(backslashes / 2, '\\');
arg.push_back('"');
}
backslashes = 0;
continue;
}
arg.append(backslashes, '\\');
backslashes = 0;
if (!inQuotes && (c == ' ' || c == '\t')) {
break;
}
arg.push_back(c);
}
arg.append(backslashes, '\\');
result.push_back(std::move(arg));
while (i < len && (input[i] == ' ' || input[i] == '\t')) {
++i;
}
}
return result;
}
}

View File

@ -1,7 +1,11 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <optional>
#include <sched.h>
#include <string>
#include <vector>
namespace processes {
struct Process {
@ -11,4 +15,8 @@ namespace processes {
void *allocProcessHandle(pid_t pid);
Process* processFromHandle(void* hHandle, bool pop);
std::optional<std::filesystem::path> resolveExecutable(const std::string &command, bool searchPath);
int spawnViaWibo(const std::filesystem::path &hostExecutable, const std::vector<std::string> &arguments, pid_t *pidOut);
std::vector<std::string> splitCommandLine(const char *commandLine);
}