diff options
Diffstat (limited to 'command.cpp')
-rw-r--r-- | command.cpp | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/command.cpp b/command.cpp new file mode 100644 index 0000000..7d62163 --- /dev/null +++ b/command.cpp @@ -0,0 +1,82 @@ +#include <stdexcept> +#include <cstring> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <sys/wait.h> +#include "command.h" + + +std::pair<int, std::string> readCommand(const std::vector<std::string> &args) { + int pipefd[2]; + if (pipe(pipefd) < 0) { + throw std::runtime_error("readCommand: pipe: " + std::string{strerror(errno)}); + } + + pid_t pid = fork(); + if (pid < 0) { + throw std::runtime_error("readCommand: fork: " + std::string{strerror(errno)}); + } + + if (pid == 0) { + close(STDOUT_FILENO); + dup2(pipefd[1], STDOUT_FILENO); + close(pipefd[0]); + close(pipefd[1]); + + std::vector<char*> argv(args.size() + 1); + for (size_t i = 0; i < args.size(); i++) { + argv[i] = new char[args[i].size() + 1]; + memcpy(argv[i], args[i].data(), args[i].size() + 1); + } + argv[args.size()] = nullptr; + + execvp(argv[0], argv.data()); + perror("execvp"); + exit(255); + } + + close(pipefd[1]); + + std::string output; + while (true) { + size_t base = output.size(); + output.resize(base + 4096); + ssize_t nr = read(pipefd[0], output.data() + base, 4096); + if (nr < 0) { + if (errno == EINTR) { + output.resize(base); + continue; + } + kill(pid, SIGTERM); + throw std::runtime_error("readCommand: read: " + std::string{strerror(errno)}); + } + output.resize(base + nr); + if (nr == 0) break; + } + + while (true) { + int status; + pid_t wpid = waitpid(pid, &status, 0); + if (wpid < 0) { + if (errno == EINTR) continue; + kill(pid, SIGTERM); + throw std::runtime_error("readCommand: wait: " + std::string{strerror(errno)}); + } + if (WIFEXITED(status)) { + return std::make_pair(WEXITSTATUS(status), output); + } + } +} + +std::string runCommand(const std::vector<std::string> &args) { + auto result = readCommand(args); + if (result.first != 0) { + std::string cmdline; + for (const std::string &a : args) cmdline += " " + a; + throw std::runtime_error( + "Command exited with exit code " + std::to_string(result.first) + ":" + cmdline + ); + } + return result.second; +} |