process_handle_signal.hpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. // Copyright (c) 2022 Klemens D. Morgenstern
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
  6. #define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
  7. #include <boost/process/v2/detail/config.hpp>
  8. #include <sys/types.h>
  9. #include <sys/wait.h>
  10. #include <boost/process/v2/detail/last_error.hpp>
  11. #include <boost/process/v2/detail/throw_error.hpp>
  12. #include <boost/process/v2/exit_code.hpp>
  13. #include <boost/process/v2/pid.hpp>
  14. #if defined(BOOST_PROCESS_V2_STANDALONE)
  15. #include <asio/any_io_executor.hpp>
  16. #include <asio/compose.hpp>
  17. #include <asio/dispatch.hpp>
  18. #include <asio/post.hpp>
  19. #include <asio/signal_set.hpp>
  20. #else
  21. #include <boost/asio/any_io_executor.hpp>
  22. #include <boost/asio/compose.hpp>
  23. #include <boost/asio/dispatch.hpp>
  24. #include <boost/asio/post.hpp>
  25. #include <boost/asio/signal_set.hpp>
  26. #endif
  27. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  28. namespace detail
  29. {
  30. template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
  31. struct basic_process_handle_signal
  32. {
  33. struct native_handle_type
  34. {
  35. native_handle_type() = delete;
  36. native_handle_type(const native_handle_type & ) = delete;
  37. ~native_handle_type() = default;
  38. };
  39. typedef Executor executor_type;
  40. executor_type get_executor()
  41. { return signal_set_.get_executor(); }
  42. /// Rebinds the process_handle to another executor.
  43. template<typename Executor1>
  44. struct rebind_executor
  45. {
  46. /// The socket type when rebound to the specified executor.
  47. typedef basic_process_handle_signal<Executor1> other;
  48. };
  49. template<typename ExecutionContext>
  50. basic_process_handle_signal(ExecutionContext &context,
  51. typename std::enable_if<
  52. std::is_convertible<ExecutionContext &,
  53. BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context &>::value
  54. >::type * = nullptr)
  55. : pid_(-1), signal_set_(context, SIGCHLD)
  56. {
  57. }
  58. basic_process_handle_signal(Executor executor)
  59. : pid_(-1), signal_set_(executor, SIGCHLD)
  60. {
  61. }
  62. basic_process_handle_signal(Executor executor, pid_type pid)
  63. : pid_(pid), signal_set_(executor, SIGCHLD)
  64. {
  65. }
  66. basic_process_handle_signal(basic_process_handle_signal && handle)
  67. : pid_(handle.pid_), signal_set_(handle.signal_set_.get_executor(), SIGCHLD)
  68. {
  69. handle.pid_ = -1;
  70. }
  71. basic_process_handle_signal& operator=(basic_process_handle_signal && handle)
  72. {
  73. pid_ = handle.id();
  74. signal_set_.~basic_signal_set();
  75. using ss = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set<Executor>;
  76. new (&signal_set_) ss(handle.get_executor(), SIGCHLD);
  77. handle.pid_ = -1;
  78. return *this;
  79. }
  80. template<typename Executor1>
  81. basic_process_handle_signal(basic_process_handle_signal<Executor1> && handle)
  82. : pid_(handle.pid_), signal_set_(Executor1(handle.signal_set_.get_executor()), SIGCHLD)
  83. {
  84. handle.pid_ = -1;
  85. }
  86. pid_type id() const { return pid_; }
  87. native_handle_type native_handle() {return {};}
  88. void terminate_if_running(error_code &)
  89. {
  90. terminate_if_running();
  91. }
  92. void terminate_if_running()
  93. {
  94. if (pid_ <= 0)
  95. return;
  96. if (::waitpid(pid_, nullptr, WNOHANG) == 0)
  97. {
  98. ::kill(pid_, SIGKILL);
  99. ::waitpid(pid_, nullptr, 0);
  100. }
  101. }
  102. void wait(native_exit_code_type &exit_status, error_code &ec)
  103. {
  104. if (pid_ <= 0)
  105. return;
  106. while (::waitpid(pid_, &exit_status, 0) < 0)
  107. {
  108. if (errno != EINTR)
  109. {
  110. ec = get_last_error();
  111. break;
  112. }
  113. }
  114. }
  115. void wait(native_exit_code_type &exit_status)
  116. {
  117. if (pid_ <= 0)
  118. return;
  119. error_code ec;
  120. wait(exit_status, ec);
  121. if (ec)
  122. detail::throw_error(ec, "wait(pid)");
  123. }
  124. void interrupt(error_code &ec)
  125. {
  126. if (pid_ <= 0)
  127. return;
  128. if (::kill(pid_, SIGINT) == -1)
  129. ec = get_last_error();
  130. }
  131. void interrupt()
  132. {
  133. if (pid_ <= 0)
  134. return;
  135. error_code ec;
  136. interrupt(ec);
  137. if (ec)
  138. detail::throw_error(ec, "interrupt");
  139. }
  140. void request_exit(error_code &ec)
  141. {
  142. if (pid_ <= 0)
  143. return;
  144. if (::kill(pid_, SIGTERM) == -1)
  145. ec = get_last_error();
  146. }
  147. void request_exit()
  148. {
  149. if (pid_ <= 0)
  150. return;
  151. error_code ec;
  152. request_exit(ec);
  153. if (ec)
  154. detail::throw_error(ec, "request_exit");
  155. }
  156. void suspend()
  157. {
  158. if (pid_ <= 0)
  159. return;
  160. error_code ec;
  161. suspend(ec);
  162. if (ec)
  163. detail::throw_error(ec, "suspend");
  164. }
  165. void suspend(error_code &ec)
  166. {
  167. if (pid_ <= 0)
  168. return;
  169. if (::kill(pid_, SIGSTOP) == -1)
  170. ec = get_last_error();
  171. }
  172. void resume()
  173. {
  174. if (pid_ <= 0)
  175. return;
  176. error_code ec;
  177. resume(ec);
  178. if (ec)
  179. detail::throw_error(ec, "resume");
  180. }
  181. void resume(error_code &ec)
  182. {
  183. if (pid_ <= 0)
  184. return;
  185. if (::kill(pid_, SIGCONT) == -1)
  186. ec = get_last_error();
  187. }
  188. void terminate(native_exit_code_type &exit_status, error_code &ec)
  189. {
  190. if (pid_ <= 0)
  191. return;
  192. if (::kill(pid_, SIGKILL) == -1)
  193. ec = get_last_error();
  194. else
  195. wait(exit_status, ec);
  196. }
  197. void terminate(native_exit_code_type &exit_status)
  198. {
  199. if (pid_ <= 0)
  200. return;
  201. error_code ec;
  202. terminate(exit_status, ec);
  203. if (ec)
  204. detail::throw_error(ec, "terminate");
  205. }
  206. bool running(native_exit_code_type &exit_code, error_code & ec)
  207. {
  208. if (pid_ <= 0)
  209. return false;
  210. int code = 0;
  211. int res = ::waitpid(pid_, &code, WNOHANG);
  212. if (res == -1)
  213. ec = get_last_error();
  214. if (res == 0)
  215. return true;
  216. else
  217. exit_code = code;
  218. return false;
  219. }
  220. bool running(native_exit_code_type &exit_code)
  221. {
  222. if (pid_ <= 0)
  223. return false;
  224. error_code ec;
  225. bool res = running(exit_code, ec);
  226. if (ec)
  227. detail::throw_error(ec, "is_running");
  228. return res;
  229. }
  230. bool is_open() const
  231. {
  232. return pid_ != -1;
  233. }
  234. template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
  235. WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
  236. BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type))
  237. async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
  238. {
  239. return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
  240. async_wait_op_{signal_set_, pid_}, handler, signal_set_);
  241. }
  242. private:
  243. template<typename>
  244. friend struct basic_process_handle_signal;
  245. pid_type pid_ = -1;
  246. BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set<Executor> signal_set_;
  247. struct async_wait_op_
  248. {
  249. BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set<Executor> &handle;
  250. pid_type pid_;
  251. template<typename Self>
  252. void operator()(Self &&self)
  253. {
  254. handle.async_wait(std::move(self));
  255. handle.cancel();
  256. // we cancel so we end up on the signal-sets executor
  257. }
  258. template<typename Self>
  259. void operator()(Self &&self, error_code ec, int sig)
  260. {
  261. if (ec == BOOST_PROCESS_V2_ASIO_NAMESPACE::error::operation_aborted &&
  262. self.get_cancellation_state().cancelled()
  263. == BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_type::none)
  264. ec.clear();
  265. native_exit_code_type exit_code = -1;
  266. int wait_res = -1;
  267. if (pid_ <= 0) // error, complete early
  268. ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor;
  269. else if (!ec)
  270. {
  271. wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
  272. if (wait_res == -1)
  273. ec = get_last_error();
  274. }
  275. if (!ec && (wait_res == 0))
  276. {
  277. handle.async_wait(std::move(self));
  278. return;
  279. }
  280. struct completer
  281. {
  282. error_code ec;
  283. native_exit_code_type code;
  284. typename std::decay<Self>::type self;
  285. void operator()()
  286. {
  287. self.complete(ec, code);
  288. }
  289. };
  290. const auto exec = self.get_executor();
  291. BOOST_PROCESS_V2_ASIO_NAMESPACE::dispatch(exec, completer{ec, exit_code, std::move(self)});
  292. }
  293. };
  294. };
  295. }
  296. BOOST_PROCESS_V2_END_NAMESPACE
  297. #endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP