summaryrefslogtreecommitdiff
path: root/src/execute.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/execute.cc')
-rw-r--r--src/execute.cc248
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;
}