]>
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 | ||
f6b5b4d7 | 6 | #include <signal.h> |
7c673cae FG |
7 | #include <sys/socket.h> |
8 | #include <sys/wait.h> | |
7c673cae FG |
9 | #include <unistd.h> |
10 | #include <sstream> | |
7c673cae | 11 | |
7c673cae | 12 | #include "common/errno.h" |
91327a77 | 13 | #include "common/safe_io.h" |
11fdf7f2 | 14 | #include "include/ceph_assert.h" |
91327a77 AA |
15 | #include "include/compat.h" |
16 | #include "include/sock_compat.h" | |
7c673cae FG |
17 | |
18 | /** | |
19 | * pre-fork fork/daemonize helper class | |
20 | * | |
21 | * Hide the details of letting a process fork early, do a bunch of | |
22 | * initialization work that may spam stdout or exit with an error, and | |
23 | * then daemonize. The exit() method will either exit directly (if we | |
24 | * haven't forked) or pass a message to the parent with the error if | |
25 | * we have. | |
26 | */ | |
27 | class Preforker { | |
28 | pid_t childpid; | |
29 | bool forked; | |
30 | int fd[2]; // parent's, child's | |
31 | ||
32 | public: | |
33 | Preforker() | |
34 | : childpid(0), | |
35 | forked(false) | |
36 | {} | |
37 | ||
38 | int prefork(std::string &err) { | |
11fdf7f2 | 39 | ceph_assert(!forked); |
7c673cae | 40 | std::ostringstream oss; |
91327a77 | 41 | int r = socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, fd); |
7c673cae | 42 | if (r < 0) { |
91327a77 AA |
43 | int e = errno; |
44 | oss << "[" << getpid() << "]: unable to create socketpair: " << cpp_strerror(e); | |
7c673cae | 45 | err = oss.str(); |
91327a77 | 46 | return (errno = e, -1); |
f6b5b4d7 TL |
47 | } |
48 | ||
49 | struct sigaction sa; | |
50 | sa.sa_handler = SIG_IGN; | |
51 | sigemptyset(&sa.sa_mask); | |
52 | sa.sa_flags = 0; | |
53 | if (sigaction(SIGHUP, &sa, nullptr) != 0) { | |
54 | int e = errno; | |
55 | oss << "[" << getpid() << "]: unable to ignore SIGHUP: " << cpp_strerror(e); | |
56 | err = oss.str(); | |
57 | return (errno = e, -1); | |
7c673cae FG |
58 | } |
59 | ||
60 | forked = true; | |
61 | ||
62 | childpid = fork(); | |
63 | if (childpid < 0) { | |
91327a77 AA |
64 | int e = errno; |
65 | oss << "[" << getpid() << "]: unable to fork: " << cpp_strerror(e); | |
7c673cae | 66 | err = oss.str(); |
91327a77 | 67 | return (errno = e, -1); |
7c673cae FG |
68 | } |
69 | if (is_child()) { | |
70 | ::close(fd[0]); | |
71 | } else { | |
72 | ::close(fd[1]); | |
73 | } | |
74 | return 0; | |
75 | } | |
76 | ||
77 | int get_signal_fd() const { | |
78 | return forked ? fd[1] : 0; | |
79 | } | |
80 | ||
81 | bool is_child() { | |
82 | return childpid == 0; | |
83 | } | |
84 | ||
85 | bool is_parent() { | |
86 | return childpid != 0; | |
87 | } | |
88 | ||
89 | int parent_wait(std::string &err_msg) { | |
11fdf7f2 | 90 | ceph_assert(forked); |
7c673cae FG |
91 | |
92 | int r = -1; | |
93 | std::ostringstream oss; | |
94 | int err = safe_read_exact(fd[0], &r, sizeof(r)); | |
95 | if (err == 0 && r == -1) { | |
96 | // daemonize | |
97 | ::close(0); | |
98 | ::close(1); | |
99 | ::close(2); | |
7c673cae FG |
100 | } else if (err) { |
101 | oss << "[" << getpid() << "]: " << cpp_strerror(err); | |
102 | } else { | |
103 | // wait for child to exit | |
104 | int status; | |
105 | err = waitpid(childpid, &status, 0); | |
106 | if (err < 0) { | |
107 | oss << "[" << getpid() << "]" << " waitpid error: " << cpp_strerror(err); | |
108 | } else if (WIFSIGNALED(status)) { | |
109 | oss << "[" << getpid() << "]" << " exited with a signal"; | |
110 | } else if (!WIFEXITED(status)) { | |
111 | oss << "[" << getpid() << "]" << " did not exit normally"; | |
112 | } else { | |
113 | err = WEXITSTATUS(status); | |
114 | if (err != 0) | |
115 | oss << "[" << getpid() << "]" << " returned exit_status " << cpp_strerror(err); | |
116 | } | |
117 | } | |
118 | err_msg = oss.str(); | |
119 | return err; | |
120 | } | |
121 | ||
122 | int signal_exit(int r) { | |
123 | if (forked) { | |
28e407b8 | 124 | /* If we get an error here, it's too late to do anything reasonable about it. */ |
11fdf7f2 | 125 | [[maybe_unused]] auto n = safe_write(fd[1], &r, sizeof(r)); |
7c673cae FG |
126 | } |
127 | return r; | |
128 | } | |
129 | void exit(int r) { | |
130 | if (is_child()) | |
131 | signal_exit(r); | |
132 | ::exit(r); | |
133 | } | |
134 | ||
135 | void daemonize() { | |
11fdf7f2 | 136 | ceph_assert(forked); |
7c673cae FG |
137 | static int r = -1; |
138 | int r2 = ::write(fd[1], &r, sizeof(r)); | |
139 | r += r2; // make the compiler shut up about the unused return code from ::write(2). | |
140 | } | |
141 | ||
142 | }; | |
143 | ||
144 | #endif |