Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / common / Preforker.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #ifndef CEPH_COMMON_PREFORKER_H
4 #define CEPH_COMMON_PREFORKER_H
5
6 #include <sys/socket.h>
7 #include <sys/wait.h>
8 #include <unistd.h>
9 #include <sstream>
10
11 #include "include/assert.h"
12 #include "common/safe_io.h"
13 #include "common/errno.h"
14
15 /**
16  * pre-fork fork/daemonize helper class
17  *
18  * Hide the details of letting a process fork early, do a bunch of
19  * initialization work that may spam stdout or exit with an error, and
20  * then daemonize.  The exit() method will either exit directly (if we
21  * haven't forked) or pass a message to the parent with the error if
22  * we have.
23  */
24 class Preforker {
25   pid_t childpid;
26   bool forked;
27   int fd[2];  // parent's, child's
28
29 public:
30   Preforker()
31     : childpid(0),
32       forked(false)
33   {}
34
35   int prefork(std::string &err) {
36     assert(!forked);
37     int r = ::socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
38     std::ostringstream oss;
39     if (r < 0) {
40       oss << "[" << getpid() << "]: unable to create socketpair: " << cpp_strerror(errno);
41       err = oss.str();
42       return r;
43     }
44
45     forked = true;
46
47     childpid = fork();
48     if (childpid < 0) {
49       r = -errno;
50       oss << "[" << getpid() << "]: unable to fork: " << cpp_strerror(errno);
51       err = oss.str();
52       return r;
53     }
54     if (is_child()) {
55       ::close(fd[0]);
56     } else {
57       ::close(fd[1]);
58     }
59     return 0;
60   }
61
62   int get_signal_fd() const {
63     return forked ? fd[1] : 0;
64   }
65
66   bool is_child() {
67     return childpid == 0;
68   }
69
70   bool is_parent() {
71     return childpid != 0;
72   }
73
74   int parent_wait(std::string &err_msg) {
75     assert(forked);
76
77     int r = -1;
78     std::ostringstream oss;
79     int err = safe_read_exact(fd[0], &r, sizeof(r));
80     if (err == 0 && r == -1) {
81       // daemonize
82       ::close(0);
83       ::close(1);
84       ::close(2);
85       r = 0;
86     } else if (err) {
87       oss << "[" << getpid() << "]: " << cpp_strerror(err);
88     } else {
89       // wait for child to exit
90       int status;
91       err = waitpid(childpid, &status, 0);
92       if (err < 0) {
93         oss << "[" << getpid() << "]" << " waitpid error: " << cpp_strerror(err);
94       } else if (WIFSIGNALED(status)) {
95         oss << "[" << getpid() << "]" << " exited with a signal";
96       } else if (!WIFEXITED(status)) {
97         oss << "[" << getpid() << "]" << " did not exit normally";
98       } else {
99         err = WEXITSTATUS(status);
100         if (err != 0)
101          oss << "[" << getpid() << "]" << " returned exit_status " << cpp_strerror(err);
102       }
103     }
104     err_msg = oss.str();
105     return err;
106   }
107
108   int signal_exit(int r) {
109     if (forked) {
110       // tell parent.  this shouldn't fail, but if it does, pass the
111       // error back to the parent.
112       int ret = safe_write(fd[1], &r, sizeof(r));
113       if (ret <= 0)
114         return ret;
115     }
116     return r;
117   }
118   void exit(int r) {
119     if (is_child())
120         signal_exit(r);
121     ::exit(r);
122   }
123
124   void daemonize() {
125     assert(forked);
126     static int r = -1;
127     int r2 = ::write(fd[1], &r, sizeof(r));
128     r += r2;  // make the compiler shut up about the unused return code from ::write(2).
129   }
130   
131 };
132
133 #endif