pdfork_launcher.hpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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&, BOOST_PROCESS_V2_ASIO_NAMESPACE::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&, BOOST_PROCESS_V2_ASIO_NAMESPACE::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. BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
  48. BOOST_PROCESS_V2_ASIO_NAMESPACE::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. BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
  64. BOOST_PROCESS_V2_ASIO_NAMESPACE::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 = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
  90. exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
  91. ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare);
  92. pid = ::pdfork(&fd, PD_DAEMON | PD_CLOEXEC);
  93. if (pid == -1)
  94. {
  95. ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
  96. detail::on_fork_error(*this, executable, argv, ec, inits...);
  97. detail::on_error(*this, executable, argv, ec, inits...);
  98. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category())
  99. return basic_process<Executor>{exec};
  100. }
  101. else if (pid == 0)
  102. {
  103. ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child);
  104. ::close(pg.p[0]);
  105. ec = detail::on_exec_setup(*this, executable, argv, inits...);
  106. if (!ec)
  107. {
  108. close_all_fds(ec);
  109. }
  110. if (!ec)
  111. ::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
  112. default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
  113. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category())
  114. detail::on_exec_error(*this, executable, argv, ec, inits...);
  115. ::exit(EXIT_FAILURE);
  116. return basic_process<Executor>{exec};
  117. }
  118. ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
  119. ::close(pg.p[1]);
  120. pg.p[1] = -1;
  121. int child_error{0};
  122. int count = -1;
  123. while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
  124. {
  125. int err = errno;
  126. if ((err != EAGAIN) && (err != EINTR))
  127. {
  128. BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category())
  129. break;
  130. }
  131. }
  132. if (count != 0)
  133. BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category())
  134. if (ec)
  135. {
  136. detail::on_error(*this, executable, argv, ec, inits...);
  137. return basic_process<Executor>{exec};
  138. }
  139. }
  140. basic_process<Executor> proc(exec, pid, fd);
  141. detail::on_success(*this, executable, argv, ec, inits...);
  142. return proc;
  143. }
  144. };
  145. }
  146. BOOST_PROCESS_V2_END_NAMESPACE
  147. #endif //BOOST_PROCESS_V2_POSIX_PDFORK_LAUNCHER_HPP