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