File indexing completed on 2025-01-18 09:50:06
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef BOOST_PROCESS_DETAIL_POSIX_WAIT_FOR_EXIT_HPP
0011 #define BOOST_PROCESS_DETAIL_POSIX_WAIT_FOR_EXIT_HPP
0012
0013 #include <boost/process/detail/config.hpp>
0014 #include <boost/process/detail/posix/child_handle.hpp>
0015 #include <system_error>
0016 #include <sys/types.h>
0017 #include <sys/wait.h>
0018 #include <unistd.h>
0019
0020 namespace boost { namespace process { namespace detail { namespace posix {
0021
0022 inline void wait(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
0023 {
0024 pid_t ret;
0025 int status;
0026
0027 do
0028 {
0029 ret = ::waitpid(p.pid, &status, 0);
0030 }
0031 while (((ret == -1) && (errno == EINTR)) ||
0032 (ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
0033
0034 if (ret == -1)
0035 ec = boost::process::detail::get_last_error();
0036 else
0037 {
0038 ec.clear();
0039 exit_code = status;
0040 }
0041 }
0042
0043 inline void wait(const child_handle &p, int & exit_code) noexcept
0044 {
0045 std::error_code ec;
0046 wait(p, exit_code, ec);
0047 boost::process::detail::throw_error(ec, "waitpid(2) failed in wait");
0048 }
0049
0050 template< class Clock, class Duration >
0051 inline bool wait_until(
0052 const child_handle &p,
0053 int & exit_code,
0054 const std::chrono::time_point<Clock, Duration>& time_out,
0055 std::error_code & ec) noexcept
0056 {
0057 ::sigset_t sigset;
0058
0059
0060
0061 using _signal_t = void(*)(int);
0062 static thread_local _signal_t sigchld_handler = SIG_DFL;
0063
0064 struct signal_interceptor_t
0065 {
0066 static void handler_func(int val)
0067 {
0068 if ((sigchld_handler != SIG_DFL) && (sigchld_handler != SIG_IGN))
0069 sigchld_handler(val);
0070 }
0071 signal_interceptor_t() { sigchld_handler = ::signal(SIGCHLD, &handler_func); }
0072 ~signal_interceptor_t() { ::signal(SIGCHLD, sigchld_handler); sigchld_handler = SIG_DFL;}
0073
0074 } signal_interceptor{};
0075
0076 if (sigemptyset(&sigset) != 0)
0077 {
0078 ec = get_last_error();
0079 return false;
0080 }
0081 if (sigaddset(&sigset, SIGCHLD) != 0)
0082 {
0083 ec = get_last_error();
0084 return false;
0085 }
0086
0087 auto get_timespec =
0088 [](const Duration & dur)
0089 {
0090 ::timespec ts;
0091 ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
0092 ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() % 1000000000;
0093 return ts;
0094 };
0095
0096 int ret;
0097 int status{0};
0098
0099 struct ::sigaction old_sig;
0100 if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
0101 {
0102 ec = get_last_error();
0103 return false;
0104 }
0105
0106 bool timed_out;
0107
0108 #if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
0109 do
0110 {
0111 auto ts = get_timespec(time_out - Clock::now());
0112 auto ret_sig = ::sigtimedwait(&sigset, nullptr, &ts);
0113 errno = 0;
0114 ret = ::waitpid(p.pid, &status, WNOHANG);
0115
0116 if ((ret_sig == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
0117 old_sig.sa_handler(ret);
0118
0119 if (ret == 0)
0120 {
0121 timed_out = Clock::now() >= time_out;
0122 if (timed_out)
0123 return false;
0124 }
0125 }
0126 while ((ret == 0) ||
0127 (((ret == -1) && errno == EINTR) ||
0128 ((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
0129 #else
0130
0131 pid_t timeout_pid = ::fork();
0132 if (timeout_pid == -1)
0133 {
0134 ec = boost::process::detail::get_last_error();
0135 return true;
0136 }
0137 else if (timeout_pid == 0)
0138 {
0139 auto ts = get_timespec(time_out - Clock::now());
0140 ::timespec rem;
0141 while (ts.tv_sec > 0 || ts.tv_nsec > 0)
0142 {
0143 if (::nanosleep(&ts, &rem) != 0)
0144 {
0145 auto err = errno;
0146 if ((err == EINVAL) || (err == EFAULT))
0147 break;
0148 }
0149 ts = get_timespec(time_out - Clock::now());
0150 }
0151 ::exit(0);
0152 }
0153
0154 struct child_cleaner_t
0155 {
0156 pid_t pid;
0157 ~child_cleaner_t()
0158 {
0159 int res;
0160 ::kill(pid, SIGKILL);
0161 ::waitpid(pid, &res, 0);
0162 }
0163 };
0164 child_cleaner_t child_cleaner{timeout_pid};
0165
0166 do
0167 {
0168 int sig_{0};
0169 if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
0170 && (WIFEXITED(status) || WIFSIGNALED(status)))
0171
0172 return false;
0173
0174 ret = ::sigwait(&sigset, &sig_);
0175 errno = 0;
0176
0177 if ((sig_ == SIGCHLD) &&
0178 (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
0179 old_sig.sa_handler(ret);
0180
0181 ret = ::waitpid(p.pid, &status, WNOHANG);
0182 if (ret == 0)
0183 {
0184 timed_out = Clock::now() >= time_out;
0185 if (timed_out)
0186 return false;
0187 }
0188 }
0189 while ((ret == 0) ||
0190 (((ret == -1) && errno == EINTR) ||
0191 ((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
0192 #endif
0193
0194 if (ret == -1)
0195 ec = boost::process::detail::get_last_error();
0196 else
0197 {
0198 ec.clear();
0199 exit_code = status;
0200 }
0201
0202 return true;
0203 }
0204
0205 template< class Clock, class Duration >
0206 inline bool wait_until(
0207 const child_handle &p,
0208 int & exit_code,
0209 const std::chrono::time_point<Clock, Duration>& time_out)
0210 {
0211 std::error_code ec;
0212 bool b = wait_until(p, exit_code, time_out, ec);
0213 boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_until");
0214 return b;
0215 }
0216
0217 template< class Rep, class Period >
0218 inline bool wait_for(
0219 const child_handle &p,
0220 int & exit_code,
0221 const std::chrono::duration<Rep, Period>& rel_time,
0222 std::error_code & ec) noexcept
0223 {
0224 return wait_until(p, exit_code, std::chrono::steady_clock::now() + rel_time, ec);
0225 }
0226
0227 template< class Rep, class Period >
0228 inline bool wait_for(
0229 const child_handle &p,
0230 int & exit_code,
0231 const std::chrono::duration<Rep, Period>& rel_time)
0232 {
0233 std::error_code ec;
0234 bool b = wait_for(p, exit_code, rel_time, ec);
0235 boost::process::detail::throw_error(ec, "waitpid(2) failed in wait_for");
0236 return b;
0237 }
0238
0239 }}}}
0240
0241 #endif