]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/Preforker.h
import 15.2.5
[ceph.git] / ceph / src / common / Preforker.h
CommitLineData
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 */
27class Preforker {
28 pid_t childpid;
29 bool forked;
30 int fd[2]; // parent's, child's
31
32public:
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