File indexing completed on 2026-08-27 09:14:54
0001
0002
0003
0004
0005 #ifndef BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
0006 #define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
0007
0008 #include <boost/process/v2/detail/config.hpp>
0009
0010 #include <sys/types.h>
0011 #include <sys/wait.h>
0012
0013 #include <boost/process/v2/detail/last_error.hpp>
0014 #include <boost/process/v2/detail/throw_error.hpp>
0015 #include <boost/process/v2/exit_code.hpp>
0016 #include <boost/process/v2/pid.hpp>
0017
0018 #if defined(BOOST_PROCESS_V2_STANDALONE)
0019 #include <asio/any_io_executor.hpp>
0020 #include <asio/append.hpp>
0021 #include <asio/associated_immediate_executor.hpp>
0022 #include <asio/compose.hpp>
0023 #include <asio/dispatch.hpp>
0024 #include <asio/post.hpp>
0025 #if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
0026 #include <asio/signal_set.hpp>
0027 #endif
0028 #else
0029 #include <boost/asio/any_io_executor.hpp>
0030 #include <boost/asio/append.hpp>
0031 #include <boost/asio/associated_immediate_executor.hpp>
0032 #include <boost/asio/compose.hpp>
0033 #include <boost/asio/dispatch.hpp>
0034 #include <boost/asio/post.hpp>
0035 #if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
0036 #include <boost/asio/signal_set.hpp>
0037 #endif
0038 #endif
0039
0040 BOOST_PROCESS_V2_BEGIN_NAMESPACE
0041
0042 namespace detail
0043 {
0044
0045 template<typename Executor = net::any_io_executor>
0046 struct basic_process_handle_signal
0047 {
0048 struct native_handle_type
0049 {
0050 native_handle_type() = delete;
0051 native_handle_type(const native_handle_type & ) = delete;
0052 ~native_handle_type() = default;
0053 };
0054
0055 typedef Executor executor_type;
0056
0057 executor_type get_executor()
0058 { return signal_set_.get_executor(); }
0059
0060
0061 template<typename Executor1>
0062 struct rebind_executor
0063 {
0064
0065 typedef basic_process_handle_signal<Executor1> other;
0066 };
0067
0068 template<typename ExecutionContext>
0069 basic_process_handle_signal(ExecutionContext &context,
0070 typename std::enable_if<
0071 std::is_convertible<ExecutionContext &,
0072 net::execution_context &>::value
0073 >::type * = nullptr)
0074 : pid_(-1), signal_set_(context, SIGCHLD)
0075 {
0076 }
0077
0078 basic_process_handle_signal(Executor executor)
0079 : pid_(-1), signal_set_(executor, SIGCHLD)
0080 {
0081 }
0082
0083 basic_process_handle_signal(Executor executor, pid_type pid)
0084 : pid_(pid), signal_set_(executor, SIGCHLD)
0085 {
0086 }
0087
0088 basic_process_handle_signal(basic_process_handle_signal && handle)
0089 : pid_(handle.pid_), signal_set_(handle.signal_set_.get_executor(), SIGCHLD)
0090 {
0091 handle.pid_ = -1;
0092 }
0093
0094 basic_process_handle_signal& operator=(basic_process_handle_signal && handle)
0095 {
0096 pid_ = handle.id();
0097 #if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
0098
0099 signal_set_.~basic_signal_set();
0100 using ss = net::basic_signal_set<Executor>;
0101 new (&signal_set_) ss(handle.get_executor(), SIGCHLD);
0102 #else
0103 signal_set_.executor = handle.signal_set_.executor;
0104 #endif
0105 handle.pid_ = -1;
0106 return *this;
0107 }
0108
0109
0110 template<typename Executor1>
0111 basic_process_handle_signal(basic_process_handle_signal<Executor1> && handle)
0112 : pid_(handle.pid_), signal_set_(Executor1(handle.signal_set_.get_executor()), SIGCHLD)
0113 {
0114 handle.pid_ = -1;
0115 }
0116
0117 pid_type id() const { return pid_; }
0118 native_handle_type native_handle() {return {};}
0119
0120 void terminate_if_running(error_code &)
0121 {
0122 terminate_if_running();
0123 }
0124
0125 void terminate_if_running()
0126 {
0127 if (pid_ <= 0)
0128 return;
0129 if (::waitpid(pid_, nullptr, WNOHANG) == 0)
0130 {
0131 ::kill(pid_, SIGKILL);
0132 ::waitpid(pid_, nullptr, 0);
0133 }
0134 }
0135
0136 void wait(native_exit_code_type &exit_status, error_code &ec)
0137 {
0138 if (pid_ <= 0)
0139 return;
0140 while (::waitpid(pid_, &exit_status, 0) < 0)
0141 {
0142 if (errno != EINTR)
0143 {
0144 ec = get_last_error();
0145 break;
0146 }
0147 }
0148 }
0149
0150 void wait(native_exit_code_type &exit_status)
0151 {
0152 if (pid_ <= 0)
0153 return;
0154 error_code ec;
0155 wait(exit_status, ec);
0156 if (ec)
0157 detail::throw_error(ec, "wait(pid)");
0158 }
0159
0160 void interrupt(error_code &ec)
0161 {
0162 if (pid_ <= 0)
0163 return;
0164 if (::kill(pid_, SIGINT) == -1)
0165 ec = get_last_error();
0166 }
0167
0168 void interrupt()
0169 {
0170 if (pid_ <= 0)
0171 return;
0172 error_code ec;
0173 interrupt(ec);
0174 if (ec)
0175 detail::throw_error(ec, "interrupt");
0176 }
0177
0178 void request_exit(error_code &ec)
0179 {
0180 if (pid_ <= 0)
0181 return;
0182 if (::kill(pid_, SIGTERM) == -1)
0183 ec = get_last_error();
0184 }
0185
0186 void request_exit()
0187 {
0188 if (pid_ <= 0)
0189 return;
0190 error_code ec;
0191 request_exit(ec);
0192 if (ec)
0193 detail::throw_error(ec, "request_exit");
0194 }
0195
0196 void suspend()
0197 {
0198 if (pid_ <= 0)
0199 return;
0200 error_code ec;
0201 suspend(ec);
0202 if (ec)
0203 detail::throw_error(ec, "suspend");
0204 }
0205
0206 void suspend(error_code &ec)
0207 {
0208 if (pid_ <= 0)
0209 return;
0210 if (::kill(pid_, SIGSTOP) == -1)
0211 ec = get_last_error();
0212 }
0213
0214 void resume()
0215 {
0216 if (pid_ <= 0)
0217 return;
0218 error_code ec;
0219 resume(ec);
0220 if (ec)
0221 detail::throw_error(ec, "resume");
0222 }
0223
0224 void resume(error_code &ec)
0225 {
0226 if (pid_ <= 0)
0227 return;
0228 if (::kill(pid_, SIGCONT) == -1)
0229 ec = get_last_error();
0230 }
0231
0232 void terminate(native_exit_code_type &exit_status, error_code &ec)
0233 {
0234 if (pid_ <= 0)
0235 return;
0236 if (::kill(pid_, SIGKILL) == -1)
0237 ec = get_last_error();
0238 else
0239 wait(exit_status, ec);
0240 }
0241
0242 void terminate(native_exit_code_type &exit_status)
0243 {
0244 if (pid_ <= 0)
0245 return;
0246 error_code ec;
0247 terminate(exit_status, ec);
0248 if (ec)
0249 detail::throw_error(ec, "terminate");
0250 }
0251
0252 bool running(native_exit_code_type &exit_code, error_code & ec)
0253 {
0254 if (pid_ <= 0)
0255 return false;
0256 int code = 0;
0257 int res = ::waitpid(pid_, &code, WNOHANG);
0258 if (res == -1)
0259 ec = get_last_error();
0260 else if (res == 0)
0261 return true;
0262 else
0263 {
0264 ec.clear();
0265 exit_code = code;
0266 }
0267 return false;
0268 }
0269
0270 bool running(native_exit_code_type &exit_code)
0271 {
0272 if (pid_ <= 0)
0273 return false;
0274
0275 error_code ec;
0276 bool res = running(exit_code, ec);
0277 if (ec)
0278 detail::throw_error(ec, "is_running");
0279 return res;
0280 }
0281
0282 bool is_open() const
0283 {
0284 return pid_ != -1;
0285 }
0286
0287 private:
0288 template<typename>
0289 friend struct basic_process_handle_signal;
0290 pid_type pid_ = -1;
0291 #if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
0292 net::basic_signal_set<Executor> signal_set_;
0293 #else
0294 struct signal_set_dummy_
0295 {
0296 signal_set_dummy_(signal_set_dummy_ &&) = default;
0297 signal_set_dummy_(const signal_set_dummy_ &) = default;
0298 Executor executor;
0299 using executor_type = Executor;
0300 executor_type get_executor() {return executor;}
0301 signal_set_dummy_(Executor executor, int) : executor(std::move(executor)) {}
0302 };
0303 signal_set_dummy_ signal_set_;
0304 #endif
0305 struct async_wait_op_
0306 {
0307 #if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
0308
0309 net::basic_signal_set<Executor> &handle;
0310 pid_type pid_;
0311 native_exit_code_type & exit_code;
0312
0313 template<typename Self>
0314 void operator()(Self &&self)
0315 {
0316 self.reset_cancellation_state(asio::enable_total_cancellation());
0317 handle.async_wait(std::move(self));
0318 handle.cancel();
0319
0320 }
0321
0322 template<typename Self>
0323 void operator()(Self &&self, error_code ec, int )
0324 {
0325 if (ec == net::error::operation_aborted &&
0326 self.get_cancellation_state().cancelled()
0327 == net::cancellation_type::none)
0328 ec.clear();
0329
0330 int wait_res = -1;
0331
0332 if (pid_ <= 0)
0333 ec = net::error::bad_descriptor;
0334 else if (!ec && process_is_running(exit_code))
0335 {
0336 wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
0337 if (wait_res == -1)
0338 ec = get_last_error();
0339 }
0340
0341 if (!ec && (wait_res == 0))
0342 {
0343 handle.async_wait(std::move(self));
0344 return;
0345 }
0346
0347 const auto exec = self.get_executor();
0348 net::dispatch(exec, net::append(std::move(self), ec));
0349 }
0350 #else
0351 signal_set_dummy_ dummy_;
0352 pid_t pid;
0353 template<typename Self>
0354 void operator()(Self &&self)
0355 {
0356 auto exec = net::get_associated_immediate_executor(self, dummy_.get_executor());
0357 error_code ec;
0358 BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported);
0359 net::dispatch(exec, net::append(std::move(self), native_exit_code_type(), ec));
0360 }
0361 #endif
0362 template<typename Self>
0363 void operator()(Self &&self, error_code ec)
0364 {
0365 self.complete(ec);
0366 }
0367 };
0368 public:
0369 template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
0370 WaitHandler = net::default_completion_token_t<executor_type>>
0371 auto async_wait(native_exit_code_type & exit_code,
0372 WaitHandler &&handler = net::default_completion_token_t<executor_type>())
0373 -> decltype(net::async_compose<WaitHandler, void(error_code)>(
0374 async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_))
0375 {
0376 return net::async_compose<WaitHandler, void(error_code)>(
0377 async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_);
0378 }
0379 };
0380
0381 }
0382
0383
0384 BOOST_PROCESS_V2_END_NAMESPACE
0385
0386
0387 #endif