travis: fix clang
[awesomized/libmemcached] / testing / lib / ForkAndExec.cpp
index b882eb435e7da935596bedd9e0e70758ee267d91..5274dba99dc48745c8b5c1fecae37f6d48607668 100644 (file)
@@ -8,47 +8,88 @@
 #include <unistd.h>
 
 ForkAndExec::ForkAndExec(const char *binary_, char **argv_)
-: binary{binary_}
+: ready{-1, -1}
+, pipes{-1, -1}
+, binary{binary_}
 , argv{argv_}
 {
-  if (pipe2(pipes, O_CLOEXEC|O_NONBLOCK)) {
-    int error = errno;
-    perror("Server::start pipe2()");
-    throw system_error(error, system_category());
-  }
 }
 
 ForkAndExec::~ForkAndExec() {
-  if (pipes[0] != -1) {
-    close(pipes[0]);
-  }
-  if (pipes[1] != -1) {
-    close(pipes[1]);
+  for (auto &pipe : {ready, pipes}) {
+    for (auto m : {READ, WRITE}) {
+      closePipe(pipe[m]);
+    }
   }
 }
 
-optional<pid_t> ForkAndExec::operator()()  {
-  if (pipes[0] == -1) {
-    return {};
+int ForkAndExec::createPipe() {
+  if (pipes[mode::READ] == -1) {
+    if(pipe2(pipes, O_NONBLOCK)) {
+      perror("ForkAndExec pipe()");
+      return -1;
+    }
   }
-  if (pipes[1] != -1) {
-    close(pipes[1]);
-    pipes[1] = -1;
+
+  return pipes[mode::READ];
+}
+
+pid_t ForkAndExec::operator()()  {
+  if (!prepareExecReadyPipe()) {
+    return 0;
   }
 
   switch (pid_t pid = fork()) {
     case 0:
+      closePipe(pipes[mode::READ]);
+      prepareOutputPipe();
       execvp(binary, argv);
-      [[fallthrough]];
+      perror("ForkAndExec exec()");
+      _exit(EXIT_FAILURE);
+
     case -1:
-      perror("fork() && exec()");
-      return {};
+      perror("ForkAndExec fork()");
+      return 0;
 
     default:
-      pollfd fd{pipes[0], 0, 0};
-      if (1 > poll(&fd, 1, 5000)) {
-        cerr << "exec() timed out" << endl;
-      }
+      pipes[mode::READ] = -1;
+      closePipe(pipes[mode::WRITE]);
+      pollExecReadyPipe();
       return pid;
   }
 }
+
+bool ForkAndExec::prepareExecReadyPipe() {
+  if (ready[mode::READ] == -1) {
+    if (pipe2(ready, O_CLOEXEC | O_NONBLOCK)) {
+      perror("ForkAndExec pipe()");
+      return false;
+    }
+    closePipe(ready[mode::WRITE]);
+  }
+
+  return true;
+}
+
+void ForkAndExec::prepareOutputPipe() {
+  if (pipes[mode::WRITE] != -1) {
+    if (0 > dup2(pipes[mode::WRITE], STDERR_FILENO) ||
+        0 > dup2(pipes[mode::WRITE], STDOUT_FILENO)) {
+      perror("ForkAndExec dup()");
+    }
+  }
+}
+
+void ForkAndExec::closePipe(int &fd) {
+  if (fd != -1) {
+    close(fd);
+    fd = -1;
+  }
+}
+
+void ForkAndExec::pollExecReadyPipe() {
+  pollfd fd{ready[mode::READ], 0, 0};
+  if (1 > poll(&fd, 1, 5000)) {
+    cerr << "exec() timed out" << endl;
+  }
+}