]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/Preforker.h
update sources to v12.1.0
[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
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 */
24class Preforker {
25 pid_t childpid;
26 bool forked;
27 int fd[2]; // parent's, child's
28
29public:
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) {
110 // tell parent. this shouldn't fail, but if it does, pass the
111 // error back to the parent.
112 int ret = safe_write(fd[1], &r, sizeof(r));
113 if (ret <= 0)
114 return ret;
115 }
116 return r;
117 }
118 void exit(int r) {
119 if (is_child())
120 signal_exit(r);
121 ::exit(r);
122 }
123
124 void daemonize() {
125 assert(forked);
126 static int r = -1;
127 int r2 = ::write(fd[1], &r, sizeof(r));
128 r += r2; // make the compiler shut up about the unused return code from ::write(2).
129 }
130
131};
132
133#endif