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.
11 #include <sys/types.h>
14 #include "include/ceph_assert.h"
15 #include "common/errno.h"
17 static void _fork_function_dummy_sighandler(int sig
) {}
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.
23 static inline int fork_function(
26 std::function
<int8_t(void)> f
)
28 // first fork the forker.
29 pid_t forker_pid
= fork();
33 while (waitpid(forker_pid
, &status
, 0) == -1) {
34 ceph_assert(errno
== EINTR
);
36 if (WIFSIGNALED(status
)) {
37 errstr
<< ": got signal: " << WTERMSIG(status
) << "\n";
38 return 128 + WTERMSIG(status
);
40 if (WIFEXITED(status
)) {
41 int8_t r
= WEXITSTATUS(status
);
42 errstr
<< ": exit status: " << (int)r
<< "\n";
45 errstr
<< ": waitpid: unknown status returned\n";
49 // we are forker (first child)
52 int maxfd
= sysconf(_SC_OPEN_MAX
);
55 for (int fd
= 0; fd
<= maxfd
; fd
++) {
56 if (fd
== STDIN_FILENO
)
58 if (fd
== STDOUT_FILENO
)
60 if (fd
== STDERR_FILENO
)
65 sigset_t mask
, oldmask
;
68 // Restore default action for SIGTERM in case the parent process decided
70 if (signal(SIGTERM
, SIG_DFL
) == SIG_ERR
) {
71 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
74 // Because SIGCHLD is ignored by default, setup dummy handler for it,
76 if (signal(SIGCHLD
, _fork_function_dummy_sighandler
) == SIG_ERR
) {
77 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
80 // Setup timeout handler.
81 if (signal(SIGALRM
, timeout_sighandler
) == SIG_ERR
) {
82 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
85 // Block interesting signals.
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";
100 std::cerr
<< ": fork failed: " << cpp_strerror(errno
) << "\n";
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";
111 (void)setpgid(0, 0); // Become process group leader.
117 (void)alarm(timeout
);
121 if (sigwait(&mask
, &signo
) == -1) {
122 std::cerr
<< ": sigwait failed: " << cpp_strerror(errno
) << "\n";
128 if (waitpid(pid
, &status
, WNOHANG
) == -1) {
129 std::cerr
<< ": waitpid failed: " << cpp_strerror(errno
) << "\n";
132 if (WIFEXITED(status
))
133 _exit(WEXITSTATUS(status
));
134 if (WIFSIGNALED(status
))
135 _exit(128 + WTERMSIG(status
));
136 std::cerr
<< ": unknown status returned\n";
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";
148 std::cerr
<< ": timed out (" << timeout
<< " sec)\n";
149 if (::killpg(pid
, SIGKILL
) == -1) {
150 std::cerr
<< ": kill failed: " << cpp_strerror(errno
) << "\n";
155 std::cerr
<< ": sigwait: invalid signal: " << signo
<< "\n";