process_handle_fd.hpp 9.1 KB

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