default_launcher.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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_DEFAULT_LAUNCHER
  6. #define BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER
  7. #include <boost/process/v2/detail/config.hpp>
  8. #include <boost/process/v2/cstring_ref.hpp>
  9. #include <boost/process/v2/posix/detail/close_handles.hpp>
  10. #include <boost/process/v2/detail/throw_error.hpp>
  11. #include <boost/process/v2/detail/utf8.hpp>
  12. #if defined(BOOST_PROCESS_V2_STANDALONE)
  13. #include <asio/execution/executor.hpp>
  14. #include <asio/is_executor.hpp>
  15. #include <asio/execution_context.hpp>
  16. #include <asio/execution/context.hpp>
  17. #include <asio/query.hpp>
  18. #else
  19. #include <boost/asio/execution/executor.hpp>
  20. #include <boost/asio/is_executor.hpp>
  21. #include <boost/asio/execution_context.hpp>
  22. #include <boost/asio/execution/context.hpp>
  23. #include <boost/asio/query.hpp>
  24. #endif
  25. #include <fcntl.h>
  26. #include <unistd.h>
  27. #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__)
  28. extern "C" { extern char **environ; }
  29. #endif
  30. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  31. template<typename Executor>
  32. struct basic_process;
  33. namespace posix
  34. {
  35. namespace detail
  36. {
  37. struct base {};
  38. struct derived : base {};
  39. template<typename Launcher, typename Init>
  40. inline error_code invoke_on_setup(Launcher & launcher, const filesystem::path &executable,
  41. const char * const * (&cmd_line),
  42. Init && init, base && )
  43. {
  44. return error_code{};
  45. }
  46. template<typename Launcher, typename Init>
  47. inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable,
  48. const char * const * (&cmd_line),
  49. Init && init, derived && )
  50. -> decltype(init.on_setup(launcher, executable, cmd_line))
  51. {
  52. return init.on_setup(launcher, executable, cmd_line);
  53. }
  54. template<typename Launcher>
  55. inline error_code on_setup(Launcher & launcher, const filesystem::path &executable,
  56. const char * const * (&cmd_line))
  57. {
  58. return error_code{};
  59. }
  60. template<typename Launcher, typename Init1, typename ... Inits>
  61. inline error_code on_setup(Launcher & launcher, const filesystem::path &executable,
  62. const char * const * (&cmd_line),
  63. Init1 && init1, Inits && ... inits)
  64. {
  65. auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{});
  66. if (ec)
  67. return ec;
  68. else
  69. return on_setup(launcher, executable, cmd_line, inits...);
  70. }
  71. template<typename Launcher, typename Init>
  72. inline void invoke_on_error(Launcher & launcher, const filesystem::path &executable,
  73. const char * const * (&cmd_line),
  74. const error_code & ec, Init && init, base && )
  75. {
  76. }
  77. template<typename Launcher, typename Init>
  78. inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable,
  79. const char * const * (&cmd_line),
  80. const error_code & ec, Init && init, derived && )
  81. -> decltype(init.on_error(launcher, ec, executable, cmd_line, ec))
  82. {
  83. init.on_error(launcher, executable, cmd_line, ec);
  84. }
  85. template<typename Launcher>
  86. inline void on_error(Launcher & launcher, const filesystem::path &executable,
  87. const char * const * (&cmd_line),
  88. const error_code & ec)
  89. {
  90. }
  91. template<typename Launcher, typename Init1, typename ... Inits>
  92. inline void on_error(Launcher & launcher, const filesystem::path &executable,
  93. const char * const * (&cmd_line),
  94. const error_code & ec,
  95. Init1 && init1, Inits && ... inits)
  96. {
  97. invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{});
  98. on_error(launcher, executable, cmd_line, ec, inits...);
  99. }
  100. template<typename Launcher, typename Init>
  101. inline void invoke_on_success(Launcher & launcher, const filesystem::path &executable,
  102. const char * const * (&cmd_line),
  103. Init && init, base && )
  104. {
  105. }
  106. template<typename Launcher, typename Init>
  107. inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable,
  108. const char * const * (&cmd_line),
  109. Init && init, derived && )
  110. -> decltype(init.on_success(launcher, executable, cmd_line))
  111. {
  112. init.on_success(launcher, executable, cmd_line);
  113. }
  114. template<typename Launcher>
  115. inline void on_success(Launcher & launcher, const filesystem::path &executable,
  116. const char * const * (&cmd_line))
  117. {
  118. }
  119. template<typename Launcher, typename Init1, typename ... Inits>
  120. inline void on_success(Launcher & launcher, const filesystem::path &executable,
  121. const char * const * (&cmd_line),
  122. Init1 && init1, Inits && ... inits)
  123. {
  124. invoke_on_success(launcher, executable, cmd_line, init1, derived{});
  125. on_success(launcher, executable, cmd_line, inits...);
  126. }
  127. template<typename Launcher, typename Init>
  128. inline void invoke_on_fork_error(Launcher & launcher, const filesystem::path &executable,
  129. const char * const * (&cmd_line),
  130. const error_code & ec, Init && init, base && )
  131. {
  132. }
  133. template<typename Launcher, typename Init>
  134. inline auto invoke_on_fork_error(Launcher & launcher, const filesystem::path &executable,
  135. const char * const * (&cmd_line),
  136. const error_code & ec, Init && init, derived && )
  137. -> decltype(init.on_fork_error(launcher, ec, executable, cmd_line, ec))
  138. {
  139. init.on_fork_error(launcher, executable, cmd_line, ec);
  140. }
  141. template<typename Launcher>
  142. inline void on_fork_error(Launcher & launcher, const filesystem::path &executable,
  143. const char * const * (&cmd_line),
  144. const error_code & ec)
  145. {
  146. }
  147. template<typename Launcher, typename Init1, typename ... Inits>
  148. inline void on_fork_error(Launcher & launcher, const filesystem::path &executable,
  149. const char * const * (&cmd_line),
  150. const error_code & ec,
  151. Init1 && init1, Inits && ... inits)
  152. {
  153. invoke_on_fork_error(launcher, executable, cmd_line, ec, init1, derived{});
  154. on_fork_error(launcher, executable, cmd_line, ec, inits...);
  155. }
  156. template<typename Launcher, typename Init>
  157. inline void invoke_on_fork_success(Launcher & launcher, const filesystem::path &executable,
  158. const char * const * (&cmd_line),
  159. Init && init, base && )
  160. {
  161. }
  162. template<typename Launcher, typename Init>
  163. inline auto invoke_on_fork_success(Launcher & launcher, const filesystem::path &executable,
  164. const char * const * (&cmd_line),
  165. Init && init, derived && )
  166. -> decltype(init.on_fork_success(launcher, executable, cmd_line))
  167. {
  168. init.on_fork_success(launcher, executable, cmd_line);
  169. }
  170. template<typename Launcher>
  171. inline void on_fork_success(Launcher & launcher, const filesystem::path &executable,
  172. const char * const * (&cmd_line))
  173. {
  174. }
  175. template<typename Launcher, typename Init1, typename ... Inits>
  176. inline void on_fork_success(Launcher & launcher, const filesystem::path &executable,
  177. const char * const * (&cmd_line),
  178. Init1 && init1, Inits && ... inits)
  179. {
  180. invoke_on_fork_success(launcher, executable, cmd_line, init1, derived{});
  181. on_fork_success(launcher, executable, cmd_line, inits...);
  182. }
  183. template<typename Launcher, typename Init>
  184. inline error_code invoke_on_exec_setup(Launcher & launcher, const filesystem::path &executable,
  185. const char * const * (&cmd_line),
  186. Init && init, base && )
  187. {
  188. return error_code{};
  189. }
  190. template<typename Launcher, typename Init>
  191. inline auto invoke_on_exec_setup(Launcher & launcher, const filesystem::path &executable,
  192. const char * const * (&cmd_line),
  193. Init && init, derived && )
  194. -> decltype(init.on_exec_setup(launcher, executable, cmd_line))
  195. {
  196. return init.on_exec_setup(launcher, executable, cmd_line);
  197. }
  198. template<typename Launcher>
  199. inline error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable,
  200. const char * const * (&cmd_line))
  201. {
  202. return error_code{};
  203. }
  204. template<typename Launcher, typename Init1, typename ... Inits>
  205. inline error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable,
  206. const char * const * (&cmd_line),
  207. Init1 && init1, Inits && ... inits)
  208. {
  209. auto ec = invoke_on_exec_setup(launcher, executable, cmd_line, init1, derived{});
  210. if (ec)
  211. return ec;
  212. else
  213. return on_exec_setup(launcher, executable, cmd_line, inits...);
  214. }
  215. template<typename Launcher, typename Init>
  216. inline void invoke_on_exec_error(Launcher & launcher, const filesystem::path &executable,
  217. const char * const * (&cmd_line),
  218. const error_code & ec, Init && init, base && )
  219. {
  220. }
  221. template<typename Launcher, typename Init>
  222. inline auto invoke_on_exec_error(Launcher & launcher, const filesystem::path &executable,
  223. const char * const * (&cmd_line),
  224. const error_code & ec, Init && init, derived && )
  225. -> decltype(init.on_exec_error(launcher, ec, executable, cmd_line, ec))
  226. {
  227. init.on_exec_error(launcher, executable, cmd_line, ec);
  228. }
  229. template<typename Launcher>
  230. inline void on_exec_error(Launcher & launcher, const filesystem::path &executable,
  231. const char * const * (&cmd_line),
  232. const error_code & ec)
  233. {
  234. }
  235. template<typename Launcher, typename Init1, typename ... Inits>
  236. inline void on_exec_error(Launcher & launcher, const filesystem::path &executable,
  237. const char * const * (&cmd_line),
  238. const error_code & ec,
  239. Init1 && init1, Inits && ... inits)
  240. {
  241. invoke_on_exec_error(launcher, executable, cmd_line, ec, init1, derived{});
  242. on_exec_error(launcher, executable, cmd_line, ec, inits...);
  243. }
  244. }
  245. /// The default launcher for processes on windows.
  246. struct default_launcher
  247. {
  248. /// The pointer to the environment forwarded to the subprocess.
  249. const char * const * env = ::environ;
  250. /// The pid of the subprocess - will be assigned after fork.
  251. int pid = -1;
  252. /// The whitelist for file descriptors.
  253. std::vector<int> fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
  254. default_launcher() = default;
  255. template<typename ExecutionContext, typename Args, typename ... Inits>
  256. auto operator()(ExecutionContext & context,
  257. const typename std::enable_if<std::is_convertible<
  258. ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
  259. filesystem::path >::type & executable,
  260. Args && args,
  261. Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
  262. {
  263. error_code ec;
  264. auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  265. if (ec)
  266. v2::detail::throw_error(ec, "default_launcher");
  267. return proc;
  268. }
  269. template<typename ExecutionContext, typename Args, typename ... Inits>
  270. auto operator()(ExecutionContext & context,
  271. error_code & ec,
  272. const typename std::enable_if<std::is_convertible<
  273. ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
  274. filesystem::path >::type & executable,
  275. Args && args,
  276. Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
  277. {
  278. return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  279. }
  280. template<typename Executor, typename Args, typename ... Inits>
  281. auto operator()(Executor exec,
  282. const typename std::enable_if<
  283. BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
  284. BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
  285. filesystem::path >::type & executable,
  286. Args && args,
  287. Inits && ... inits ) -> basic_process<Executor>
  288. {
  289. error_code ec;
  290. auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  291. if (ec)
  292. v2::detail::throw_error(ec, "default_launcher");
  293. return proc;
  294. }
  295. template<typename Executor, typename Args, typename ... Inits>
  296. auto operator()(Executor exec,
  297. error_code & ec,
  298. const typename std::enable_if<
  299. BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
  300. BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
  301. filesystem::path >::type & executable,
  302. Args && args,
  303. Inits && ... inits ) -> basic_process<Executor>
  304. {
  305. auto argv = this->build_argv_(executable, std::forward<Args>(args));
  306. {
  307. pipe_guard pg;
  308. if (::pipe(pg.p))
  309. {
  310. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category())
  311. return basic_process<Executor>{exec};
  312. }
  313. if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
  314. {
  315. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category())
  316. return basic_process<Executor>{exec};
  317. }
  318. ec = detail::on_setup(*this, executable, argv, inits ...);
  319. if (ec)
  320. {
  321. detail::on_error(*this, executable, argv, ec, inits...);
  322. return basic_process<Executor>(exec);
  323. }
  324. fd_whitelist.push_back(pg.p[1]);
  325. auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
  326. exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
  327. ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare);
  328. pid = ::fork();
  329. if (pid == -1)
  330. {
  331. ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
  332. detail::on_fork_error(*this, executable, argv, ec, inits...);
  333. detail::on_error(*this, executable, argv, ec, inits...);
  334. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category())
  335. return basic_process<Executor>{exec};
  336. }
  337. else if (pid == 0)
  338. {
  339. ::close(pg.p[0]);
  340. ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child);
  341. ec = detail::on_exec_setup(*this, executable, argv, inits...);
  342. if (!ec)
  343. {
  344. close_all_fds(ec);
  345. }
  346. if (!ec)
  347. ::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
  348. ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
  349. BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category())
  350. detail::on_exec_error(*this, executable, argv, ec, inits...);
  351. ::exit(EXIT_FAILURE);
  352. return basic_process<Executor>{exec};
  353. }
  354. ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
  355. ::close(pg.p[1]);
  356. pg.p[1] = -1;
  357. int child_error{0};
  358. int count = -1;
  359. while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
  360. {
  361. int err = errno;
  362. if ((err != EAGAIN) && (err != EINTR))
  363. {
  364. BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category())
  365. break;
  366. }
  367. }
  368. if (count != 0)
  369. BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category())
  370. if (ec)
  371. {
  372. detail::on_error(*this, executable, argv, ec, inits...);
  373. return basic_process<Executor>{exec};
  374. }
  375. }
  376. basic_process<Executor> proc(exec, pid);
  377. detail::on_success(*this, executable, argv, ec, inits...);
  378. return proc;
  379. }
  380. protected:
  381. void ignore_unused(std::size_t ) {}
  382. void close_all_fds(error_code & ec)
  383. {
  384. std::sort(fd_whitelist.begin(), fd_whitelist.end());
  385. detail::close_all(fd_whitelist, ec);
  386. fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
  387. }
  388. struct pipe_guard
  389. {
  390. int p[2];
  391. pipe_guard() : p{-1,-1} {}
  392. ~pipe_guard()
  393. {
  394. if (p[0] != -1)
  395. ::close(p[0]);
  396. if (p[1] != -1)
  397. ::close(p[1]);
  398. }
  399. };
  400. //if we need to allocate something
  401. std::vector<std::string> argv_buffer_;
  402. std::vector<const char *> argv_;
  403. template<typename Args>
  404. const char * const * build_argv_(const filesystem::path & pt, const Args & args,
  405. typename std::enable_if<
  406. std::is_convertible<
  407. decltype(*std::begin(std::declval<Args>())),
  408. cstring_ref>::value>::type * = nullptr)
  409. {
  410. const auto arg_cnt = std::distance(std::begin(args), std::end(args));
  411. argv_.reserve(arg_cnt + 2);
  412. argv_.push_back(pt.native().data());
  413. for (auto && arg : args)
  414. argv_.push_back(arg.c_str());
  415. argv_.push_back(nullptr);
  416. return argv_.data();
  417. }
  418. const char * const * build_argv_(const filesystem::path &, const char ** argv)
  419. {
  420. return argv;
  421. }
  422. template<typename Args>
  423. const char * const * build_argv_(const filesystem::path & pt, const Args & args,
  424. typename std::enable_if<
  425. !std::is_convertible<
  426. decltype(*std::begin(std::declval<Args>())),
  427. cstring_ref>::value>::type * = nullptr)
  428. {
  429. const auto arg_cnt = std::distance(std::begin(args), std::end(args));
  430. argv_.reserve(arg_cnt + 2);
  431. argv_buffer_.reserve(arg_cnt);
  432. argv_.push_back(pt.native().data());
  433. using char_type = typename decay<decltype((*std::begin(std::declval<Args>()))[0])>::type;
  434. for (basic_string_view<char_type> arg : args)
  435. argv_buffer_.push_back(v2::detail::conv_string<char>(arg.data(), arg.size()));
  436. for (auto && arg : argv_buffer_)
  437. argv_.push_back(arg.c_str());
  438. argv_.push_back(nullptr);
  439. return argv_.data();
  440. }
  441. };
  442. }
  443. BOOST_PROCESS_V2_END_NAMESPACE
  444. #endif //BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER