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