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