summaryrefslogtreecommitdiff
path: root/command.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'command.cpp')
-rw-r--r--command.cpp82
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;
+}