default_launcher.hpp 18 KB

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