// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "execute.h" #include "ctor.h" #include "pointerlist.h" #include #include #include #include #include #include /* https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ https://github.com/famzah/popen-noshell/commit/1f9eaf4eeef348d1efe0f3c7fe8ab670653cfbb1 https://blog.famzah.net/2018/12/19/posix_spawn-performance-benchmarks-and-usage-examples/ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork-and-vfork/5207945#5207945 */ namespace { int parent_waitpid(pid_t pid) { int status{}; auto rc_pid = waitpid(pid, &status, 0); if(rc_pid > 0) { if(WIFEXITED(status)) { // Child exited with normally return WEXITSTATUS(status); } if(WIFSIGNALED(status)) { // Child exited via signal (segfault, abort, ...) std::cerr << strsignal(status) << '\n'; return WTERMSIG(status); } } else { // No PID returned, this is an error if(errno == ECHILD) { // No children exist. return 1; } else { // Unexpected error. abort(); } } // Should never happen... return 1; } } // namespace :: extern char **environ; // see 'man environ' int execute(const ctor::settings& settings, const std::string& command, const std::vector& args, const std::map& env, [[maybe_unused]] bool terminate) { std::vector argv; argv.push_back(command.data()); for(const auto& arg : args) { argv.push_back(arg.data()); } argv.push_back(nullptr); std::string cmd; for(const auto& arg : argv) { if(arg == nullptr) { break; } if(!cmd.empty()) { cmd += " "; } cmd += arg; } if(settings.verbose > 0) { std::cout << cmd << std::endl; } #if 1 auto pid = vfork(); if(pid == 0) { EnvMap envmap((const char**)environ); for(const auto& [key, value] : env) { envmap.insert(key + "=" + value); } if(settings.dry_run) { _exit(0); } auto [_, envv] = envmap.get(); execve(command.data(), const_cast(argv.data()), const_cast(envv)); std::cout << "Could not execute " << command << ": " << strerror(errno) << "\n"; _exit(1); // execve only returns if an error occurred } return parent_waitpid(pid); #elif 0 pid_t pid; std::vector venv; for(const auto& [key, value] : env) { venv.push_back(key + "=" + value); } Env penv(venv); if(posix_spawn(&pid, command.data(), nullptr, nullptr, (char**)argv.data(), penv.data())) { return 1; } return parent_waitpid(pid); #else (void)parent_waitpid; return system(cmd.data()); #endif return 1; }