initial code repo
[stor4nfv.git] / src / ceph / src / common / Preforker.h
diff --git a/src/ceph/src/common/Preforker.h b/src/ceph/src/common/Preforker.h
new file mode 100644 (file)
index 0000000..9fe34e5
--- /dev/null
@@ -0,0 +1,133 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_COMMON_PREFORKER_H
+#define CEPH_COMMON_PREFORKER_H
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sstream>
+
+#include "include/assert.h"
+#include "common/safe_io.h"
+#include "common/errno.h"
+
+/**
+ * pre-fork fork/daemonize helper class
+ *
+ * Hide the details of letting a process fork early, do a bunch of
+ * initialization work that may spam stdout or exit with an error, and
+ * then daemonize.  The exit() method will either exit directly (if we
+ * haven't forked) or pass a message to the parent with the error if
+ * we have.
+ */
+class Preforker {
+  pid_t childpid;
+  bool forked;
+  int fd[2];  // parent's, child's
+
+public:
+  Preforker()
+    : childpid(0),
+      forked(false)
+  {}
+
+  int prefork(std::string &err) {
+    assert(!forked);
+    int r = ::socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+    std::ostringstream oss;
+    if (r < 0) {
+      oss << "[" << getpid() << "]: unable to create socketpair: " << cpp_strerror(errno);
+      err = oss.str();
+      return r;
+    }
+
+    forked = true;
+
+    childpid = fork();
+    if (childpid < 0) {
+      r = -errno;
+      oss << "[" << getpid() << "]: unable to fork: " << cpp_strerror(errno);
+      err = oss.str();
+      return r;
+    }
+    if (is_child()) {
+      ::close(fd[0]);
+    } else {
+      ::close(fd[1]);
+    }
+    return 0;
+  }
+
+  int get_signal_fd() const {
+    return forked ? fd[1] : 0;
+  }
+
+  bool is_child() {
+    return childpid == 0;
+  }
+
+  bool is_parent() {
+    return childpid != 0;
+  }
+
+  int parent_wait(std::string &err_msg) {
+    assert(forked);
+
+    int r = -1;
+    std::ostringstream oss;
+    int err = safe_read_exact(fd[0], &r, sizeof(r));
+    if (err == 0 && r == -1) {
+      // daemonize
+      ::close(0);
+      ::close(1);
+      ::close(2);
+      r = 0;
+    } else if (err) {
+      oss << "[" << getpid() << "]: " << cpp_strerror(err);
+    } else {
+      // wait for child to exit
+      int status;
+      err = waitpid(childpid, &status, 0);
+      if (err < 0) {
+        oss << "[" << getpid() << "]" << " waitpid error: " << cpp_strerror(err);
+      } else if (WIFSIGNALED(status)) {
+        oss << "[" << getpid() << "]" << " exited with a signal";
+      } else if (!WIFEXITED(status)) {
+        oss << "[" << getpid() << "]" << " did not exit normally";
+      } else {
+        err = WEXITSTATUS(status);
+        if (err != 0)
+         oss << "[" << getpid() << "]" << " returned exit_status " << cpp_strerror(err);
+      }
+    }
+    err_msg = oss.str();
+    return err;
+  }
+
+  int signal_exit(int r) {
+    if (forked) {
+      // tell parent.  this shouldn't fail, but if it does, pass the
+      // error back to the parent.
+      int ret = safe_write(fd[1], &r, sizeof(r));
+      if (ret <= 0)
+       return ret;
+    }
+    return r;
+  }
+  void exit(int r) {
+    if (is_child())
+        signal_exit(r);
+    ::exit(r);
+  }
+
+  void daemonize() {
+    assert(forked);
+    static int r = -1;
+    int r2 = ::write(fd[1], &r, sizeof(r));
+    r += r2;  // make the compiler shut up about the unused return code from ::write(2).
+  }
+  
+};
+
+#endif