mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
Add --cmdline arg; rework wibo subprocess spawn
This commit is contained in:
parent
9dd65bc70a
commit
7f9d141a20
@ -23,8 +23,9 @@ Supported command line options:
|
||||
|
||||
- `--help`: Print usage information.
|
||||
- `-D`, `--debug`: Enable shim debug logging (equivalent to `WIBO_DEBUG=1`).
|
||||
- `-C DIR`, `--chdir DIR`, `--chdir=DIR`: Change to `DIR` before running the program.
|
||||
- `--`: Stop option parsing; following arguments are interpreted as the program command line.
|
||||
- `-C DIR`, `--chdir DIR`, `--chdir=DIR`: Change to `DIR` before running the guest program.
|
||||
- `--cmdline STRING`, `--cmdline=STRING`: Use `STRING` as the exact guest command line. (Including the program name as the first argument.)
|
||||
- `--`: Stop option parsing; following arguments are interpreted as the exact guest command line. (Including the program name as the first argument.)
|
||||
|
||||
## Tests
|
||||
|
||||
|
@ -682,24 +682,17 @@ namespace kernel32 {
|
||||
|
||||
bool useSearchPath = lpApplicationName == nullptr;
|
||||
std::string application;
|
||||
std::vector<std::string> arguments = processes::splitCommandLine(lpCommandLine);
|
||||
std::string commandLine = lpCommandLine ? lpCommandLine : "";
|
||||
if (lpApplicationName) {
|
||||
application = lpApplicationName;
|
||||
} else {
|
||||
std::vector<std::string> arguments = processes::splitCommandLine(commandLine.c_str());
|
||||
if (arguments.empty()) {
|
||||
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||
return 0;
|
||||
}
|
||||
application = arguments.front();
|
||||
}
|
||||
if (arguments.empty()) {
|
||||
arguments.push_back(application);
|
||||
}
|
||||
DEBUG_LOG(" -> args:");
|
||||
for (const auto &arg : arguments) {
|
||||
DEBUG_LOG(" '%s'", arg.c_str());
|
||||
}
|
||||
DEBUG_LOG("\n");
|
||||
|
||||
auto resolved = processes::resolveExecutable(application, useSearchPath);
|
||||
if (!resolved) {
|
||||
@ -708,7 +701,7 @@ namespace kernel32 {
|
||||
}
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = processes::spawnViaWibo(*resolved, arguments, &pid);
|
||||
int spawnResult = processes::spawnWithCommandLine(*resolved, commandLine, &pid);
|
||||
if (spawnResult != 0) {
|
||||
wibo::lastError = (spawnResult == ENOENT) ? ERROR_FILE_NOT_FOUND : ERROR_ACCESS_DENIED;
|
||||
return 0;
|
||||
|
@ -2688,16 +2688,10 @@ namespace msvcrt {
|
||||
DEBUG_LOG("_wspawnvp(%d, %s)\n", mode, command.c_str());
|
||||
|
||||
std::vector<std::string> argStorage;
|
||||
argStorage.emplace_back(command);
|
||||
for (const uint16_t *const *cursor = argv; *cursor; ++cursor) {
|
||||
argStorage.emplace_back(wideStringToString(*cursor));
|
||||
}
|
||||
if (argStorage.empty()) {
|
||||
argStorage.emplace_back(command);
|
||||
}
|
||||
DEBUG_LOG("-> argv:");
|
||||
for (const auto &arg : argStorage) {
|
||||
DEBUG_LOG(" '%s'", arg.c_str());
|
||||
}
|
||||
|
||||
auto resolved = processes::resolveExecutable(command, false);
|
||||
if (!resolved) {
|
||||
@ -2708,10 +2702,10 @@ namespace msvcrt {
|
||||
DEBUG_LOG("-> resolved to %s\n", resolved->c_str());
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = processes::spawnViaWibo(*resolved, argStorage, &pid);
|
||||
int spawnResult = processes::spawnWithArgv(*resolved, argStorage, &pid);
|
||||
if (spawnResult != 0) {
|
||||
errno = spawnResult;
|
||||
DEBUG_LOG("-> spawnViaWibo failed: %d\n", spawnResult);
|
||||
DEBUG_LOG("-> spawnWithArgv failed: %d\n", spawnResult);
|
||||
return -1;
|
||||
}
|
||||
DEBUG_LOG("-> spawned pid %d\n", pid);
|
||||
@ -2752,16 +2746,10 @@ namespace msvcrt {
|
||||
DEBUG_LOG("_spawnvp(%d, %s)\n", mode, command.c_str());
|
||||
|
||||
std::vector<std::string> argStorage;
|
||||
argStorage.emplace_back(command);
|
||||
for (const char * const *cursor = argv; *cursor; ++cursor) {
|
||||
argStorage.emplace_back(*cursor);
|
||||
}
|
||||
if (argStorage.empty()) {
|
||||
argStorage.emplace_back(command);
|
||||
}
|
||||
DEBUG_LOG("-> argv:");
|
||||
for (const auto &arg : argStorage) {
|
||||
DEBUG_LOG(" '%s'", arg.c_str());
|
||||
}
|
||||
|
||||
auto resolved = processes::resolveExecutable(command, false);
|
||||
if (!resolved) {
|
||||
@ -2772,10 +2760,10 @@ namespace msvcrt {
|
||||
DEBUG_LOG("-> resolved to %s\n", resolved->c_str());
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = processes::spawnViaWibo(*resolved, argStorage, &pid);
|
||||
int spawnResult = processes::spawnWithArgv(*resolved, argStorage, &pid);
|
||||
if (spawnResult != 0) {
|
||||
errno = spawnResult;
|
||||
DEBUG_LOG("-> spawnViaWibo failed: %d\n", spawnResult);
|
||||
DEBUG_LOG("-> spawnWithArgv failed: %d\n", spawnResult);
|
||||
return -1;
|
||||
}
|
||||
DEBUG_LOG("-> spawned pid %d\n", pid);
|
||||
|
130
main.cpp
130
main.cpp
@ -98,7 +98,9 @@ static void printHelp(const char *argv0) {
|
||||
fprintf(stdout, " --help\t\tShow this help message and exit\n");
|
||||
fprintf(stdout, " -C, --chdir DIR\tChange working directory before launching the program\n");
|
||||
fprintf(stdout, " -D, --debug\tEnable shim debug logging (same as WIBO_DEBUG=1)\n");
|
||||
fprintf(stdout, " --\t\tStop option parsing; following arguments are interpreted as the program command line\n");
|
||||
fprintf(stdout, " --cmdline STRING\tUse STRING as the exact guest command line\n");
|
||||
fprintf(stdout,
|
||||
" --\t\tStop option parsing; following arguments are interpreted as the exact guest command line\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -208,6 +210,7 @@ int main(int argc, char **argv) {
|
||||
bool optionDebug = false;
|
||||
bool parsingOptions = true;
|
||||
int programIndex = -1;
|
||||
std::string cmdLine;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const char *arg = argv[i];
|
||||
@ -216,6 +219,18 @@ int main(int argc, char **argv) {
|
||||
parsingOptions = false;
|
||||
continue;
|
||||
}
|
||||
if (strncmp(arg, "--cmdline=", 10) == 0) {
|
||||
cmdLine = arg + 10;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--cmdline") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "Option %s requires a command line argument\n", arg);
|
||||
return 1;
|
||||
}
|
||||
cmdLine = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(arg, "--help") == 0) {
|
||||
printHelp(argv[0]);
|
||||
return 0;
|
||||
@ -252,7 +267,7 @@ int main(int argc, char **argv) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (programIndex == -1) {
|
||||
if (programIndex == -1 && cmdLine.empty()) {
|
||||
printHelp(argv[0]);
|
||||
return argc <= 1 ? 0 : 1;
|
||||
}
|
||||
@ -312,61 +327,76 @@ int main(int argc, char **argv) {
|
||||
|
||||
wibo::tibSelector = static_cast<uint16_t>((tibDesc.entry_number << 3) | 7);
|
||||
|
||||
char **guestArgv = argv + programIndex;
|
||||
int guestArgc = argc - programIndex;
|
||||
|
||||
const char *pePath = guestArgv[0];
|
||||
if (!pePath || pePath[0] == '\0') {
|
||||
fprintf(stderr, "No guest binary specified\n");
|
||||
// Determine the guest program name
|
||||
auto guestArgs = processes::splitCommandLine(cmdLine.c_str());
|
||||
std::string programName;
|
||||
if (programIndex != -1) {
|
||||
programName = argv[programIndex];
|
||||
} else if (!guestArgs.empty()) {
|
||||
programName = guestArgs[0];
|
||||
}
|
||||
if (programName.empty()) {
|
||||
fprintf(stderr, "No guest program specified\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string originalName = pePath;
|
||||
std::filesystem::path resolvedGuestPath = processes::resolveExecutable(originalName, true).value_or({});
|
||||
// Resolve the guest program path
|
||||
std::filesystem::path resolvedGuestPath = processes::resolveExecutable(programName, true).value_or({});
|
||||
if (resolvedGuestPath.empty()) {
|
||||
fprintf(stderr, "Failed to resolve path to guest binary %s\n", originalName.c_str());
|
||||
fprintf(stderr, "Failed to resolve path to guest program %s\n", programName.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Build guest arguments
|
||||
if (guestArgs.empty()) {
|
||||
guestArgs.push_back(files::pathToWindows(resolvedGuestPath));
|
||||
}
|
||||
for (int i = programIndex + 1; i < argc; ++i) {
|
||||
guestArgs.emplace_back(argv[i]);
|
||||
}
|
||||
|
||||
// Build a command line
|
||||
std::string cmdLine;
|
||||
for (int i = 0; i < guestArgc; ++i) {
|
||||
std::string arg;
|
||||
if (i == 0) {
|
||||
arg = files::pathToWindows(resolvedGuestPath);
|
||||
} else {
|
||||
cmdLine += ' ';
|
||||
arg = guestArgv[i];
|
||||
}
|
||||
bool needQuotes = arg.find_first_of("\" \t\n") != std::string::npos;
|
||||
if (needQuotes)
|
||||
cmdLine += '"';
|
||||
int backslashes = 0;
|
||||
for (const char *p = arg.c_str();; p++) {
|
||||
char c = *p;
|
||||
if (c == '\\') {
|
||||
backslashes++;
|
||||
continue;
|
||||
if (cmdLine.empty()) {
|
||||
for (int i = 0; i < guestArgs.size(); ++i) {
|
||||
std::string arg;
|
||||
if (i == 0) {
|
||||
arg = files::pathToWindows(resolvedGuestPath);
|
||||
} else {
|
||||
cmdLine += ' ';
|
||||
arg = guestArgs[i];
|
||||
}
|
||||
bool needQuotes = arg.find_first_of("\" \t\n") != std::string::npos;
|
||||
if (needQuotes)
|
||||
cmdLine += '"';
|
||||
int backslashes = 0;
|
||||
for (const char *p = arg.c_str();; p++) {
|
||||
char c = *p;
|
||||
if (c == '\\') {
|
||||
backslashes++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Backslashes are doubled *before quotes*
|
||||
for (int j = 0; j < backslashes; j++) {
|
||||
cmdLine += '\\';
|
||||
if (c == '\0' || c == '"')
|
||||
// Backslashes are doubled *before quotes*
|
||||
for (int j = 0; j < backslashes; j++) {
|
||||
cmdLine += '\\';
|
||||
}
|
||||
backslashes = 0;
|
||||
if (c == '\0' || c == '"')
|
||||
cmdLine += '\\';
|
||||
}
|
||||
backslashes = 0;
|
||||
|
||||
if (c == '\0')
|
||||
break;
|
||||
if (c == '\"')
|
||||
cmdLine += '\\';
|
||||
cmdLine += c;
|
||||
if (c == '\0')
|
||||
break;
|
||||
if (c == '\"')
|
||||
cmdLine += '\\';
|
||||
cmdLine += c;
|
||||
}
|
||||
if (needQuotes)
|
||||
cmdLine += '"';
|
||||
}
|
||||
if (needQuotes)
|
||||
cmdLine += '"';
|
||||
}
|
||||
cmdLine += '\0';
|
||||
if (cmdLine.empty() || cmdLine.back() != '\0') {
|
||||
cmdLine.push_back('\0');
|
||||
}
|
||||
|
||||
wibo::commandLine = cmdLine;
|
||||
wibo::commandLineW = stringToWideString(wibo::commandLine.c_str());
|
||||
@ -374,8 +404,16 @@ int main(int argc, char **argv) {
|
||||
|
||||
wibo::guestExecutablePath = resolvedGuestPath;
|
||||
wibo::executableName = executablePath;
|
||||
wibo::argv = guestArgv;
|
||||
wibo::argc = guestArgc;
|
||||
|
||||
// Build argv/argc
|
||||
std::vector<char *> guestArgv;
|
||||
guestArgv.reserve(guestArgs.size() + 1);
|
||||
for (const auto &arg : guestArgs) {
|
||||
guestArgv.push_back(const_cast<char *>(arg.c_str()));
|
||||
}
|
||||
guestArgv.push_back(nullptr);
|
||||
wibo::argv = guestArgv.data();
|
||||
wibo::argc = static_cast<int>(guestArgv.size()) - 1;
|
||||
|
||||
wibo::initializeModuleRegistry();
|
||||
|
||||
@ -401,7 +439,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
wibo::mainModule =
|
||||
wibo::registerProcessModule(std::move(executable), std::move(resolvedGuestPath), std::move(originalName));
|
||||
wibo::registerProcessModule(std::move(executable), std::move(resolvedGuestPath), std::move(programName));
|
||||
if (!wibo::mainModule || !wibo::mainModule->executable) {
|
||||
fprintf(stderr, "Failed to register process module\n");
|
||||
return 1;
|
||||
|
@ -181,32 +181,21 @@ namespace processes {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int spawnViaWibo(const std::filesystem::path &hostExecutable, const std::vector<std::string> &arguments, pid_t *pidOut) {
|
||||
if (hostExecutable.empty()) {
|
||||
return ENOENT;
|
||||
static int spawnInternal(const std::vector<std::string> &args, pid_t *pidOut) {
|
||||
std::vector<char *> argv;
|
||||
argv.reserve(args.size() + 2);
|
||||
argv.push_back(const_cast<char *>(wibo::executableName.c_str()));
|
||||
for (auto &arg : args) {
|
||||
argv.push_back(const_cast<char *>(arg.c_str()));
|
||||
}
|
||||
|
||||
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(const_cast<char *>(wibo::executableName.c_str()));
|
||||
for (auto &entry : storage) {
|
||||
nativeArgs.push_back(const_cast<char *>(entry.c_str()));
|
||||
}
|
||||
nativeArgs.push_back(nullptr);
|
||||
argv.push_back(nullptr);
|
||||
|
||||
DEBUG_LOG("Spawning process: %s, args: [", wibo::executableName.c_str());
|
||||
for (size_t i = 0; i < storage.size(); ++i) {
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (i != 0) {
|
||||
DEBUG_LOG(", ");
|
||||
}
|
||||
DEBUG_LOG("'%s'", storage[i].c_str());
|
||||
DEBUG_LOG("'%s'", args[i].c_str());
|
||||
}
|
||||
DEBUG_LOG("]\n");
|
||||
|
||||
@ -217,7 +206,7 @@ namespace processes {
|
||||
setenv("WIBO_DEBUG_INDENT", indent.c_str(), 1);
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = posix_spawn(&pid, wibo::executableName.c_str(), &actions, nullptr, nativeArgs.data(), environ);
|
||||
int spawnResult = posix_spawn(&pid, wibo::executableName.c_str(), &actions, nullptr, argv.data(), environ);
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
if (spawnResult != 0) {
|
||||
return spawnResult;
|
||||
@ -228,6 +217,42 @@ namespace processes {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spawnWithCommandLine(const std::string &applicationName, const std::string &commandLine, pid_t *pidOut) {
|
||||
if (wibo::executableName.empty() || (applicationName.empty() && commandLine.empty())) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.reserve(3);
|
||||
if (!commandLine.empty()) {
|
||||
args.emplace_back("--cmdline");
|
||||
args.push_back(commandLine);
|
||||
}
|
||||
if (!applicationName.empty()) {
|
||||
args.push_back(applicationName);
|
||||
}
|
||||
|
||||
return spawnInternal(args, pidOut);
|
||||
}
|
||||
|
||||
int spawnWithArgv(const std::string &applicationName, const std::vector<std::string> &argv, pid_t *pidOut) {
|
||||
if (wibo::executableName.empty() || (applicationName.empty() && argv.empty())) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.reserve(argv.size() + 1);
|
||||
if (!applicationName.empty()) {
|
||||
args.push_back(applicationName);
|
||||
}
|
||||
args.emplace_back("--");
|
||||
for (const auto &arg : argv) {
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
return spawnInternal(args, pidOut);
|
||||
}
|
||||
|
||||
std::vector<std::string> splitCommandLine(const char *commandLine) {
|
||||
std::vector<std::string> result;
|
||||
if (!commandLine) {
|
||||
|
@ -19,6 +19,7 @@ namespace processes {
|
||||
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);
|
||||
int spawnWithCommandLine(const std::string &applicationName, const std::string &commandLine, pid_t *pidOut);
|
||||
int spawnWithArgv(const std::string &applicationName, const std::vector<std::string> &argv, pid_t *pidOut);
|
||||
std::vector<std::string> splitCommandLine(const char *commandLine);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user