]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
7c673cae FG |
6 | #include <sys/socket.h> |
7 | #include <sys/wait.h> | |
7c673cae FG |
8 | #include <unistd.h> |
9 | #include <sstream> | |
7c673cae FG |
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) { | |
28e407b8 AA |
110 | /* If we get an error here, it's too late to do anything reasonable about it. */ |
111 | (void)safe_write(fd[1], &r, sizeof(r)); | |
7c673cae FG |
112 | } |
113 | return r; | |
114 | } | |
115 | void exit(int r) { | |
116 | if (is_child()) | |
117 | signal_exit(r); | |
118 | ::exit(r); | |
119 | } | |
120 | ||
121 | void daemonize() { | |
122 | assert(forked); | |
123 | static int r = -1; | |
124 | int r2 = ::write(fd[1], &r, sizeof(r)); | |
125 | r += r2; // make the compiler shut up about the unused return code from ::write(2). | |
126 | } | |
127 | ||
128 | }; | |
129 | ||
130 | #endif |