]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/fork_function.h
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / common / fork_function.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 // Run a function in a forked child, with a timeout.
5
6 #pragma once
7
8 #include <functional>
9 #include <signal.h>
10 #include <sys/wait.h>
11 #include <sys/types.h>
12 #include <ostream>
13
14 #include "include/ceph_assert.h"
15 #include "common/errno.h"
16
17 static void _fork_function_dummy_sighandler(int sig) {}
18
19 // Run a function post-fork, with a timeout. Function can return
20 // int8_t only due to unix exit code limitations. Returns -ETIMEDOUT
21 // if timeout is reached.
22
23 static inline int fork_function(
24 int timeout,
25 std::ostream& errstr,
26 std::function<int8_t(void)> f)
27 {
28 // first fork the forker.
29 pid_t forker_pid = fork();
30 if (forker_pid) {
31 // just wait
32 int status;
33 while (waitpid(forker_pid, &status, 0) == -1) {
34 ceph_assert(errno == EINTR);
35 }
36 if (WIFSIGNALED(status)) {
37 errstr << ": got signal: " << WTERMSIG(status) << "\n";
38 return 128 + WTERMSIG(status);
39 }
40 if (WIFEXITED(status)) {
41 int8_t r = WEXITSTATUS(status);
42 errstr << ": exit status: " << (int)r << "\n";
43 return r;
44 }
45 errstr << ": waitpid: unknown status returned\n";
46 return -1;
47 }
48
49 // we are forker (first child)
50
51 // close all fds
52 int maxfd = sysconf(_SC_OPEN_MAX);
53 if (maxfd == -1)
54 maxfd = 16384;
55 for (int fd = 0; fd <= maxfd; fd++) {
56 if (fd == STDIN_FILENO)
57 continue;
58 if (fd == STDOUT_FILENO)
59 continue;
60 if (fd == STDERR_FILENO)
61 continue;
62 ::close(fd);
63 }
64
65 sigset_t mask, oldmask;
66 int pid;
67
68 // Restore default action for SIGTERM in case the parent process decided
69 // to ignore it.
70 if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
71 std::cerr << ": signal failed: " << cpp_strerror(errno) << "\n";
72 goto fail_exit;
73 }
74 // Because SIGCHLD is ignored by default, setup dummy handler for it,
75 // so we can mask it.
76 if (signal(SIGCHLD, _fork_function_dummy_sighandler) == SIG_ERR) {
77 std::cerr << ": signal failed: " << cpp_strerror(errno) << "\n";
78 goto fail_exit;
79 }
80 // Setup timeout handler.
81 if (signal(SIGALRM, timeout_sighandler) == SIG_ERR) {
82 std::cerr << ": signal failed: " << cpp_strerror(errno) << "\n";
83 goto fail_exit;
84 }
85 // Block interesting signals.
86 sigemptyset(&mask);
87 sigaddset(&mask, SIGINT);
88 sigaddset(&mask, SIGTERM);
89 sigaddset(&mask, SIGCHLD);
90 sigaddset(&mask, SIGALRM);
91 if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) {
92 std::cerr << ": sigprocmask failed: "
93 << cpp_strerror(errno) << "\n";
94 goto fail_exit;
95 }
96
97 pid = fork();
98
99 if (pid == -1) {
100 std::cerr << ": fork failed: " << cpp_strerror(errno) << "\n";
101 goto fail_exit;
102 }
103
104 if (pid == 0) { // we are second child
105 // Restore old sigmask.
106 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) {
107 std::cerr << ": sigprocmask failed: "
108 << cpp_strerror(errno) << "\n";
109 goto fail_exit;
110 }
111 (void)setpgid(0, 0); // Become process group leader.
112 int8_t r = f();
113 _exit((uint8_t)r);
114 }
115
116 // Parent
117 (void)alarm(timeout);
118
119 for (;;) {
120 int signo;
121 if (sigwait(&mask, &signo) == -1) {
122 std::cerr << ": sigwait failed: " << cpp_strerror(errno) << "\n";
123 goto fail_exit;
124 }
125 switch (signo) {
126 case SIGCHLD:
127 int status;
128 if (waitpid(pid, &status, WNOHANG) == -1) {
129 std::cerr << ": waitpid failed: " << cpp_strerror(errno) << "\n";
130 goto fail_exit;
131 }
132 if (WIFEXITED(status))
133 _exit(WEXITSTATUS(status));
134 if (WIFSIGNALED(status))
135 _exit(128 + WTERMSIG(status));
136 std::cerr << ": unknown status returned\n";
137 goto fail_exit;
138 case SIGINT:
139 case SIGTERM:
140 // Pass SIGINT and SIGTERM, which are usually used to terminate
141 // a process, to the child.
142 if (::kill(pid, signo) == -1) {
143 std::cerr << ": kill failed: " << cpp_strerror(errno) << "\n";
144 goto fail_exit;
145 }
146 continue;
147 case SIGALRM:
148 std::cerr << ": timed out (" << timeout << " sec)\n";
149 if (::killpg(pid, SIGKILL) == -1) {
150 std::cerr << ": kill failed: " << cpp_strerror(errno) << "\n";
151 goto fail_exit;
152 }
153 _exit(-ETIMEDOUT);
154 default:
155 std::cerr << ": sigwait: invalid signal: " << signo << "\n";
156 goto fail_exit;
157 }
158 }
159 return 0;
160 fail_exit:
161 _exit(EXIT_FAILURE);
162 }