1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 // Run a function in a forked child, with a timeout.
16 #include <sys/types.h>
18 #include "include/ceph_assert.h"
19 #include "common/errno.h"
22 static void _fork_function_dummy_sighandler(int sig
) {}
24 // Run a function post-fork, with a timeout. Function can return
25 // int8_t only due to unix exit code limitations. Returns -ETIMEDOUT
26 // if timeout is reached.
27 static inline int fork_function(
30 std::function
<int8_t(void)> f
)
32 // first fork the forker.
33 pid_t forker_pid
= fork();
37 while (waitpid(forker_pid
, &status
, 0) == -1) {
38 ceph_assert(errno
== EINTR
);
40 if (WIFSIGNALED(status
)) {
41 errstr
<< ": got signal: " << WTERMSIG(status
) << "\n";
42 return 128 + WTERMSIG(status
);
44 if (WIFEXITED(status
)) {
45 int8_t r
= WEXITSTATUS(status
);
46 errstr
<< ": exit status: " << (int)r
<< "\n";
49 errstr
<< ": waitpid: unknown status returned\n";
53 // we are forker (first child)
56 int maxfd
= sysconf(_SC_OPEN_MAX
);
59 for (int fd
= 0; fd
<= maxfd
; fd
++) {
60 if (fd
== STDIN_FILENO
)
62 if (fd
== STDOUT_FILENO
)
64 if (fd
== STDERR_FILENO
)
69 sigset_t mask
, oldmask
;
72 // Restore default action for SIGTERM in case the parent process decided
74 if (signal(SIGTERM
, SIG_DFL
) == SIG_ERR
) {
75 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
78 // Because SIGCHLD is ignored by default, setup dummy handler for it,
80 if (signal(SIGCHLD
, _fork_function_dummy_sighandler
) == SIG_ERR
) {
81 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
84 // Setup timeout handler.
85 if (signal(SIGALRM
, timeout_sighandler
) == SIG_ERR
) {
86 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
89 // Block interesting signals.
91 sigaddset(&mask
, SIGINT
);
92 sigaddset(&mask
, SIGTERM
);
93 sigaddset(&mask
, SIGCHLD
);
94 sigaddset(&mask
, SIGALRM
);
95 if (sigprocmask(SIG_SETMASK
, &mask
, &oldmask
) == -1) {
96 std::cerr
<< ": sigprocmask failed: "
97 << cpp_strerror(errno
) << "\n";
104 std::cerr
<< ": fork failed: " << cpp_strerror(errno
) << "\n";
108 if (pid
== 0) { // we are second child
109 // Restore old sigmask.
110 if (sigprocmask(SIG_SETMASK
, &oldmask
, NULL
) == -1) {
111 std::cerr
<< ": sigprocmask failed: "
112 << cpp_strerror(errno
) << "\n";
115 (void)setpgid(0, 0); // Become process group leader.
121 (void)alarm(timeout
);
125 if (sigwait(&mask
, &signo
) == -1) {
126 std::cerr
<< ": sigwait failed: " << cpp_strerror(errno
) << "\n";
132 if (waitpid(pid
, &status
, WNOHANG
) == -1) {
133 std::cerr
<< ": waitpid failed: " << cpp_strerror(errno
) << "\n";
136 if (WIFEXITED(status
))
137 _exit(WEXITSTATUS(status
));
138 if (WIFSIGNALED(status
))
139 _exit(128 + WTERMSIG(status
));
140 std::cerr
<< ": unknown status returned\n";
144 // Pass SIGINT and SIGTERM, which are usually used to terminate
145 // a process, to the child.
146 if (::kill(pid
, signo
) == -1) {
147 std::cerr
<< ": kill failed: " << cpp_strerror(errno
) << "\n";
152 std::cerr
<< ": timed out (" << timeout
<< " sec)\n";
153 if (::killpg(pid
, SIGKILL
) == -1) {
154 std::cerr
<< ": kill failed: " << cpp_strerror(errno
) << "\n";
159 std::cerr
<< ": sigwait: invalid signal: " << signo
<< "\n";
168 static inline int fork_function(
170 std::ostream
& errstr
,
171 std::function
<int8_t(void)> f
)
173 errstr
<< "Forking is not available on Windows.\n";