pdfork_launcher.hpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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_POSIX_PDFORK_LAUNCHER_HPP
  6. #define BOOST_PROCESS_V2_POSIX_PDFORK_LAUNCHER_HPP
  7. #include <boost/process/v2/posix/default_launcher.hpp>
  8. #include <unistd.h>
  9. #include <sys/procdesc.h>
  10. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  11. namespace posix
  12. {
  13. /// A launcher using `pdfork`. Default on FreeBSD
  14. struct pdfork_launcher : default_launcher
  15. {
  16. /// The file descriptor of the subprocess. Set after fork.
  17. int fd;
  18. pdfork_launcher() = default;
  19. template<typename ExecutionContext, typename Args, typename ... Inits>
  20. auto operator()(ExecutionContext & context,
  21. const typename std::enable_if<is_convertible<
  22. ExecutionContext&, net::execution_context&>::value,
  23. filesystem::path >::type & executable,
  24. Args && args,
  25. Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
  26. {
  27. error_code ec;
  28. auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  29. if (ec)
  30. v2::detail::throw_error(ec, "pdfork_launcher");
  31. return proc;
  32. }
  33. template<typename ExecutionContext, typename Args, typename ... Inits>
  34. auto operator()(ExecutionContext & context,
  35. error_code & ec,
  36. const typename std::enable_if<is_convertible<
  37. ExecutionContext&, net::execution_context&>::value,
  38. filesystem::path >::type & executable,
  39. Args && args,
  40. Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
  41. {
  42. return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  43. }
  44. template<typename Executor, typename Args, typename ... Inits>
  45. auto operator()(Executor exec,
  46. const typename std::enable_if<
  47. net::execution::is_executor<Executor>::value ||
  48. net::is_executor<Executor>::value,
  49. filesystem::path >::type & executable,
  50. Args && args,
  51. Inits && ... inits ) -> basic_process<Executor>
  52. {
  53. error_code ec;
  54. auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  55. if (ec)
  56. v2::detail::throw_error(ec, "pdfork_launcher");
  57. return proc;
  58. }
  59. template<typename Executor, typename Args, typename ... Inits>
  60. auto operator()(Executor exec,
  61. error_code & ec,
  62. const typename std::enable_if<
  63. net::execution::is_executor<Executor>::value ||
  64. net::is_executor<Executor>::value,
  65. filesystem::path >::type & executable,
  66. Args && args,
  67. Inits && ... inits ) -> basic_process<Executor>
  68. {
  69. auto argv = this->build_argv_(executable, std::forward<Args>(args));
  70. {
  71. pipe_guard pg;
  72. if (::pipe(pg.p))
  73. {
  74. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
  75. return basic_process<Executor>{exec};
  76. }
  77. if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
  78. {
  79. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
  80. return basic_process<Executor>{exec};
  81. }
  82. ec = detail::on_setup(*this, executable, argv, inits ...);
  83. if (ec)
  84. {
  85. detail::on_error(*this, executable, argv, ec, inits...);
  86. return basic_process<Executor>(exec);
  87. }
  88. fd_whitelist.push_back(pg.p[1]);
  89. auto & ctx = net::query(
  90. exec, net::execution::context);
  91. #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
  92. ctx.notify_fork(net::execution_context::fork_prepare);
  93. #endif
  94. pid = ::pdfork(&fd, PD_DAEMON | PD_CLOEXEC);
  95. if (pid == -1)
  96. {
  97. #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
  98. ctx.notify_fork(net::execution_context::fork_parent);
  99. #endif
  100. detail::on_fork_error(*this, executable, argv, ec, inits...);
  101. detail::on_error(*this, executable, argv, ec, inits...);
  102. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
  103. return basic_process<Executor>{exec};
  104. }
  105. else if (pid == 0)
  106. {
  107. #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
  108. ctx.notify_fork(net::execution_context::fork_child);
  109. #endif
  110. ::close(pg.p[0]);
  111. ec = detail::on_exec_setup(*this, executable, argv, inits...);
  112. if (!ec)
  113. {
  114. close_all_fds(ec);
  115. }
  116. if (!ec)
  117. ::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
  118. default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
  119. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
  120. detail::on_exec_error(*this, executable, argv, ec, inits...);
  121. ::_exit(EXIT_FAILURE);
  122. return basic_process<Executor>{exec};
  123. }
  124. #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
  125. ctx.notify_fork(net::execution_context::fork_parent);
  126. #endif
  127. ::close(pg.p[1]);
  128. pg.p[1] = -1;
  129. int child_error{0};
  130. int count = -1;
  131. while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
  132. {
  133. int err = errno;
  134. if ((err != EAGAIN) && (err != EINTR))
  135. {
  136. BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category());
  137. break;
  138. }
  139. }
  140. if (count != 0)
  141. BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category());
  142. if (ec)
  143. {
  144. detail::on_error(*this, executable, argv, ec, inits...);
  145. do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
  146. return basic_process<Executor>{exec};
  147. }
  148. }
  149. basic_process<Executor> proc(exec, pid, fd);
  150. detail::on_success(*this, executable, argv, ec, inits...);
  151. return proc;
  152. }
  153. };
  154. }
  155. BOOST_PROCESS_V2_END_NAMESPACE
  156. #endif //BOOST_PROCESS_V2_POSIX_PDFORK_LAUNCHER_HPP