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