diff options
author | Bent Bisballe Nyeng <deva@aasimon.org> | 2025-02-01 20:37:32 +0100 |
---|---|---|
committer | Bent Bisballe Nyeng <deva@aasimon.org> | 2025-02-21 18:11:26 +0100 |
commit | 7b2a17216ec5cb97eeb517863d7336cc04852b43 (patch) | |
tree | 36f90d6f2cdbe8b893c9b5138460ffbdc9a129d8 /src/execute.cc | |
parent | dbacf0029104780556af5791f2c6a45edc7a5ec4 (diff) |
WIP
Diffstat (limited to 'src/execute.cc')
-rw-r--r-- | src/execute.cc | 248 |
1 files changed, 234 insertions, 14 deletions
diff --git a/src/execute.cc b/src/execute.cc index c050732..4ed274f 100644 --- a/src/execute.cc +++ b/src/execute.cc @@ -6,12 +6,24 @@ #include "ctor.h" #include "pointerlist.h" -#include <unistd.h> -#include <cstring> -#include <sys/types.h> -#include <sys/wait.h> -#include <spawn.h> +#if !defined(_WIN32) + #include <unistd.h> + #include <sys/types.h> + #include <sys/wait.h> + #include <spawn.h> + extern char **environ; +#else + #define WINDOWS_LEAN_AND_MEAN + #include <windows.h> + #undef max + #include <thread> +#endif + #include <iostream> +#include <cstring> +#include <vector> +#include <deque> +#include <filesystem> /* https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ @@ -23,7 +35,7 @@ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork- namespace { - +#if !defined(_WIN32) int parent_waitpid(pid_t pid) { int status{}; @@ -61,9 +73,80 @@ int parent_waitpid(pid_t pid) // Should never happen... return 1; } -} // namespace :: +#endif //_WIN32 + +#if defined(_WIN32) +std::string getLastErrorAsString() +{ + DWORD errorMessageID = ::GetLastError(); + if(errorMessageID == 0) + { + return {}; + } + + LPSTR message_buffer{}; + auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&message_buffer, 0, nullptr); + + std::string message(message_buffer, size); + LocalFree(message_buffer); + return message; +} + +int moveSelf(const ctor::settings& settings) +{ + int cnt{0}; + char source[MAX_PATH]; + HMODULE module = GetModuleHandle(0); + GetModuleFileNameA(module, source, MAX_PATH); + + while(true) + { + if(cnt > 10) // If we need to try more than 10 times something is wrong + { + return 1; + } + + std::filesystem::path tmp_file = settings.builddir; + tmp_file /= "tmp"; + std::string target = tmp_file.string() + "-" + std::to_string(cnt); + if(MoveFileA(source, target.data())) + { + break; // success + } + + auto err = GetLastError(); + if(err == ERROR_ALREADY_EXISTS) + { + if(DeleteFileA(target.data())) + { + continue; // Try again + } + + err = GetLastError(); + if(err != ERROR_ACCESS_DENIED) + { + std::cerr << "Could not delete file\n"; + return err; + } -extern char **environ; // see 'man environ' + cnt++; + continue; // Increment and try again + } + else + { + std::cerr << "Could not move file\n"; + return err; + } + } + return 0; +} +#endif // _WIN32 +} // namespace :: int execute(const ctor::settings& settings, const std::string& command, @@ -98,11 +181,13 @@ int execute(const ctor::settings& settings, std::cout << cmd << std::endl; } +#if !defined(_WIN32) + #if 1 auto pid = vfork(); if(pid == 0) { - EnvMap envmap((const char**)environ); + EnvMap envmap(environ); for(const auto& [key, value] : env) { envmap.insert(key + "=" + value); @@ -120,15 +205,16 @@ int execute(const ctor::settings& settings, } return parent_waitpid(pid); #elif 0 - pid_t pid; - std::vector<std::string> venv; + pid_t pid{}; + EnvMap envmap(environ); for(const auto& [key, value] : env) { - venv.push_back(key + "=" + value); + envmap.insert(key + "=" + value); } - Env penv(venv); + + auto [_, envv] = envmap.get(); if(posix_spawn(&pid, command.data(), nullptr, nullptr, - (char**)argv.data(), penv.data())) + (char**)argv.data(), const_cast<char* const *>(envv))) { return 1; } @@ -138,5 +224,139 @@ int execute(const ctor::settings& settings, return system(cmd.data()); #endif +#else // _WIN32 + if(terminate) + { + auto ret = moveSelf(settings); + if(ret != 0) + { + return ret; + } + } + + auto env_strings = GetEnvironmentStrings(); + EnvMap envmap(env_strings); + FreeEnvironmentStrings(env_strings); + for(const auto& [key, value] : env) + { + envmap.insert(key + "=" + value); + } + + // TODO: Use SetDllDirectory(...) to set DLL search directory? + + SECURITY_ATTRIBUTES security_attr; + // Set the bInheritHandle flag so pipe handles are inherited. + security_attr.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attr.bInheritHandle = TRUE; + security_attr.lpSecurityDescriptor = nullptr; + + HANDLE stream_in_read{}; + HANDLE stream_in_write{}; + HANDLE stream_out_read{}; + HANDLE stream_out_write{}; + + if(!CreatePipe(&stream_out_read, &stream_out_write, &security_attr, 0)) + { + std::cout << "Error CreatePipe (out): " << getLastErrorAsString() << "\n"; + } + + if(!SetHandleInformation(stream_out_read, HANDLE_FLAG_INHERIT, 0)) + { + std::cout << "Error - SetHandleInformation (out): " << getLastErrorAsString() << "\n"; + } + + // Create a pipe for the child process's STDIN. + if(!CreatePipe(&stream_in_read, &stream_in_write, &security_attr, 0)) + { + std::cout << "Error CreatePipe (in): " << getLastErrorAsString() << "\n"; + } + + // Ensure the write handle to the pipe for STDIN is not inherited. + if(!SetHandleInformation(stream_in_write, HANDLE_FLAG_INHERIT, 0)) + { + std::cout << "Error - SetHandleInformation (in): " << getLastErrorAsString() << "\n"; + } + + STARTUPINFO si{}; + si.hStdInput = GetStdHandle(((DWORD)-10));//STD_INPUT_HANDLE + si.hStdOutput = stream_out_write; + si.hStdError = stream_out_write; + si.dwFlags |= STARTF_USESTDHANDLES; + + PROCESS_INFORMATION pi{}; + + si.cb = sizeof(si); + + if(!CreateProcess(nullptr, // lpApplicationName + (char*)cmd.data(), // lpCommandLine + nullptr, // lpProcessAttributes + nullptr, // lpThreadAttributes + TRUE, // bInheritHandles + INHERIT_PARENT_AFFINITY | + 0x00000200 | // CREATE_NEW_PROCESS_GROUP + 0, // dwCreationFlags + envmap.stringify().data(), // lpEnvironment + nullptr, // lpCurrentDirectory + &si, // lpStartupInfo + &pi)) // lpProcessInformation + { + std::cout << "Could not execute " << command << ": " << + getLastErrorAsString() << "\n"; + return 1; + } + + int ignore_lines{}; + std::filesystem::path cmd_path{command}; + if(cmd_path.filename() == "cl.exe") + { + // Ignore first line, avoiding the annoying msvc compiler of printing the filename + // of the file that is being compiled + ignore_lines = 1; + } + + std::atomic<bool> running{true}; + auto parent_waitpid = std::thread([&](){ + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(stream_out_write); + CloseHandle(stream_in_read); + running.store(false); + }); + + CHAR buf[1024]; + while(running.load()) + { + if(WaitForSingleObject(stream_out_read, 0) != WAIT_OBJECT_0) + { + Sleep(1); + continue; + } + DWORD read_bytes{}; + auto res = ReadFile(stream_out_read, buf, sizeof(buf), &read_bytes, nullptr); + if(res != TRUE || read_bytes == 0) + { + break; + } + std::string str; + str.append(buf, read_bytes); + if(ignore_lines == 0) + { + std::cout << str << std::flush; + } + if(str.find('\n') != std::string::npos) + { + ignore_lines = std::max(0, ignore_lines - 1); + } + } + + DWORD exit_code{}; + GetExitCodeProcess(pi.hProcess, &exit_code); + parent_waitpid.join(); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return exit_code; +#endif // _WIN32 + return 1; } |