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>
12 #include <common/errno.h>
14 #include "common/errno.h"
16 static void _fork_function_dummy_sighandler(int sig
) {}
18 // Run a function post-fork, with a timeout. Function can return
19 // int8_t only due to unix exit code limitations. Returns -ETIMEDOUT
20 // if timeout is reached.
22 static inline int fork_function(
25 std::function
<int8_t(void)> f
)
27 // first fork the forker.
28 pid_t forker_pid
= fork();
32 while (waitpid(forker_pid
, &status
, 0) == -1) {
33 assert(errno
== EINTR
);
35 if (WIFSIGNALED(status
)) {
36 errstr
<< ": got signal: " << WTERMSIG(status
) << "\n";
37 return 128 + WTERMSIG(status
);
39 if (WIFEXITED(status
)) {
40 int8_t r
= WEXITSTATUS(status
);
41 errstr
<< ": exit status: " << (int)r
<< "\n";
44 errstr
<< ": waitpid: unknown status returned\n";
48 // we are forker (first child)
51 int maxfd
= sysconf(_SC_OPEN_MAX
);
54 for (int fd
= 0; fd
<= maxfd
; fd
++) {
55 if (fd
== STDIN_FILENO
)
57 if (fd
== STDOUT_FILENO
)
59 if (fd
== STDERR_FILENO
)
64 sigset_t mask
, oldmask
;
67 // Restore default action for SIGTERM in case the parent process decided
69 if (signal(SIGTERM
, SIG_DFL
) == SIG_ERR
) {
70 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
73 // Because SIGCHLD is ignored by default, setup dummy handler for it,
75 if (signal(SIGCHLD
, _fork_function_dummy_sighandler
) == SIG_ERR
) {
76 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
79 // Setup timeout handler.
80 if (signal(SIGALRM
, timeout_sighandler
) == SIG_ERR
) {
81 std::cerr
<< ": signal failed: " << cpp_strerror(errno
) << "\n";
84 // Block interesting signals.
86 sigaddset(&mask
, SIGINT
);
87 sigaddset(&mask
, SIGTERM
);
88 sigaddset(&mask
, SIGCHLD
);
89 sigaddset(&mask
, SIGALRM
);
90 if (sigprocmask(SIG_SETMASK
, &mask
, &oldmask
) == -1) {
91 std::cerr
<< ": sigprocmask failed: "
92 << cpp_strerror(errno
) << "\n";
99 std::cerr
<< ": fork failed: " << cpp_strerror(errno
) << "\n";
103 if (pid
== 0) { // we are second child
104 // Restore old sigmask.
105 if (sigprocmask(SIG_SETMASK
, &oldmask
, NULL
) == -1) {
106 std::cerr
<< ": sigprocmask failed: "
107 << cpp_strerror(errno
) << "\n";
110 (void)setpgid(0, 0); // Become process group leader.
116 (void)alarm(timeout
);
120 if (sigwait(&mask
, &signo
) == -1) {
121 std::cerr
<< ": sigwait failed: " << cpp_strerror(errno
) << "\n";
127 if (waitpid(pid
, &status
, WNOHANG
) == -1) {
128 std::cerr
<< ": waitpid failed: " << cpp_strerror(errno
) << "\n";
131 if (WIFEXITED(status
))
132 _exit(WEXITSTATUS(status
));
133 if (WIFSIGNALED(status
))
134 _exit(128 + WTERMSIG(status
));
135 std::cerr
<< ": unknown status returned\n";
139 // Pass SIGINT and SIGTERM, which are usually used to terminate
140 // a process, to the child.
141 if (::kill(pid
, signo
) == -1) {
142 std::cerr
<< ": kill failed: " << cpp_strerror(errno
) << "\n";
147 std::cerr
<< ": timed out (" << timeout
<< " sec)\n";
148 if (::killpg(pid
, SIGKILL
) == -1) {
149 std::cerr
<< ": kill failed: " << cpp_strerror(errno
) << "\n";
154 std::cerr
<< ": sigwait: invalid signal: " << signo
<< "\n";