52inline std::vector<std::string> build_environment(
const core::LaunchContract& contract) {
53 std::vector<std::string> env;
56 for (
const auto& [key, value] : contract.environment) {
57 env.push_back(key +
"=" + value);
61 if (!contract.execution.library_paths.empty()) {
63 char sep = core::get_path_separator();
65 for (
size_t i = 0; i < contract.execution.library_paths.size(); i++) {
66 if (i > 0) lib_path += sep;
67 lib_path += contract.execution.library_paths[i];
71 std::string lib_key = contract.execution.library_path_env_key;
74 if (e.find(lib_key +
"=") == 0) {
76 std::string existing = e.substr(lib_key.size() + 1);
77 e = lib_key +
"=" + lib_path + sep + existing;
84 env.push_back(lib_key +
"=" + lib_path);
98inline std::vector<std::string> build_argv(
const core::LaunchContract& contract) {
99 std::vector<std::string> argv;
100 argv.push_back(contract.execution.binary);
101 for (
const auto& arg : contract.execution.arguments) {
119inline ExecResult execute_unix(
const core::LaunchContract& contract,
bool wait_for_exit =
true) {
122 auto argv_strings = build_argv(contract);
123 auto env_strings = build_environment(contract);
126 std::vector<char*> argv;
127 for (
auto& s : argv_strings) {
128 argv.push_back(
const_cast<char*
>(s.c_str()));
130 argv.push_back(
nullptr);
132 std::vector<char*> envp;
133 for (
auto& s : env_strings) {
134 envp.push_back(
const_cast<char*
>(s.c_str()));
136 envp.push_back(
nullptr);
141 result.error =
"fork failed: " + std::string(strerror(errno));
149 if (!contract.execution.cwd.empty()) {
150 if (chdir(contract.execution.cwd.c_str()) != 0) {
156 execve(contract.execution.binary.c_str(), argv.data(), envp.data());
165 if (waitpid(pid, &status, 0) == -1) {
166 result.error =
"waitpid failed: " + std::string(strerror(errno));
170 if (WIFEXITED(status)) {
171 result.exit_code = WEXITSTATUS(status);
173 }
else if (WIFSIGNALED(status)) {
174 result.exit_code = 128 + WTERMSIG(status);
177 result.error =
"process terminated abnormally";
181 result.exit_code = 0;
192inline ExecResult exec_replace_unix(
const core::LaunchContract& contract) {
195 auto argv_strings = build_argv(contract);
196 auto env_strings = build_environment(contract);
198 std::vector<char*> argv;
199 for (
auto& s : argv_strings) {
200 argv.push_back(
const_cast<char*
>(s.c_str()));
202 argv.push_back(
nullptr);
204 std::vector<char*> envp;
205 for (
auto& s : env_strings) {
206 envp.push_back(
const_cast<char*
>(s.c_str()));
208 envp.push_back(
nullptr);
211 if (!contract.execution.cwd.empty()) {
212 if (chdir(contract.execution.cwd.c_str()) != 0) {
213 result.error =
"chdir failed: " + std::string(strerror(errno));
219 execve(contract.execution.binary.c_str(), argv.data(), envp.data());
222 result.error =
"execve failed: " + std::string(strerror(errno));
237inline std::string build_command_line(
const std::vector<std::string>& argv) {
239 for (
size_t i = 0; i < argv.size(); i++) {
240 if (i > 0) cmd +=
" ";
243 bool needs_quotes = argv[i].find(
' ') != std::string::npos ||
244 argv[i].find(
'\t') != std::string::npos;
246 if (needs_quotes) cmd +=
"\"";
248 if (needs_quotes) cmd +=
"\"";
256inline std::string build_environment_block(
const std::vector<std::string>& env) {
258 for (
const auto& e : env) {
269inline ExecResult execute_windows(
const core::LaunchContract& contract,
bool wait_for_exit =
true) {
272 auto argv = build_argv(contract);
273 auto env = build_environment(contract);
275 std::string cmd_line = build_command_line(argv);
276 std::string env_block = build_environment_block(env);
278 STARTUPINFOA si = {0};
281 PROCESS_INFORMATION pi = {0};
283 BOOL success = CreateProcessA(
284 contract.execution.binary.c_str(),
285 const_cast<char*
>(cmd_line.c_str()),
290 const_cast<char*
>(env_block.c_str()),
291 contract.execution.cwd.empty() ?
nullptr : contract.execution.cwd.c_str(),
297 result.error =
"CreateProcess failed: " + std::to_string(GetLastError());
302 WaitForSingleObject(pi.hProcess, INFINITE);
305 if (GetExitCodeProcess(pi.hProcess, &exit_code)) {
306 result.exit_code =
static_cast<int>(exit_code);
309 result.error =
"GetExitCodeProcess failed";
313 result.exit_code = 0;
316 CloseHandle(pi.hProcess);
317 CloseHandle(pi.hThread);
337inline ExecResult execute(
const core::LaunchContract& contract,
bool wait_for_exit =
true) {
339 return execute_windows(contract, wait_for_exit);
341 return execute_unix(contract, wait_for_exit);
353inline ExecResult exec_replace(
const core::LaunchContract& contract) {
355 auto result = execute_windows(contract,
false);
361 return exec_replace_unix(contract);