default_launcher.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. //
  2. // boost/process/v2/windows/default_launcher.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
  11. #define BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
  12. #include <boost/process/v2/cstring_ref.hpp>
  13. #include <boost/process/v2/detail/config.hpp>
  14. #include <boost/process/v2/detail/last_error.hpp>
  15. #include <boost/process/v2/detail/throw_error.hpp>
  16. #include <boost/process/v2/detail/utf8.hpp>
  17. #include <boost/process/v2/error.hpp>
  18. #include <numeric>
  19. #include <memory>
  20. #include <type_traits>
  21. #include <windows.h>
  22. #if defined(BOOST_PROCESS_V2_STANDALONE)
  23. #include <asio/execution/executor.hpp>
  24. #include <asio/is_executor.hpp>
  25. #include <asio/execution_context.hpp>
  26. #else
  27. #include <boost/asio/execution/executor.hpp>
  28. #include <boost/asio/is_executor.hpp>
  29. #include <boost/asio/execution_context.hpp>
  30. #endif
  31. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  32. template<typename Executor>
  33. struct basic_process;
  34. namespace detail
  35. {
  36. struct base {};
  37. struct derived : base {};
  38. template<typename Launcher, typename Init>
  39. inline error_code invoke_on_setup(Launcher & /*launcher*/, const filesystem::path &executable, std::wstring &cmd_line,
  40. Init && /*init*/, base && )
  41. {
  42. return error_code{};
  43. }
  44. template<typename Launcher, typename Init>
  45. inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  46. Init && init, derived && )
  47. -> decltype(init.on_setup(launcher, executable, cmd_line))
  48. {
  49. return init.on_setup(launcher, executable, cmd_line);
  50. }
  51. template<typename Launcher, typename Init>
  52. inline std::false_type probe_on_setup(
  53. Launcher & launcher, Init && init, base && );
  54. template<typename Launcher, typename Init>
  55. inline auto probe_on_setup(Launcher & launcher, Init && init, derived && )
  56. -> std::is_same<error_code, decltype(init.on_setup(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
  57. template<typename Launcher, typename Init>
  58. using has_on_setup = decltype(probe_on_setup(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
  59. template<typename Launcher>
  60. inline error_code on_setup(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/)
  61. {
  62. return error_code{};
  63. }
  64. template<typename Launcher, typename Init1, typename ... Inits>
  65. inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  66. Init1 && init1, Inits && ... inits)
  67. {
  68. auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{});
  69. if (ec)
  70. return ec;
  71. else
  72. return on_setup(launcher, executable, cmd_line, inits...);
  73. }
  74. template<typename Launcher, typename Init>
  75. inline void invoke_on_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/,
  76. const error_code & /*ec*/, Init && /*init*/, base && )
  77. {
  78. }
  79. template<typename Launcher, typename Init>
  80. inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  81. const error_code & ec, Init && init, derived && )
  82. -> decltype(init.on_error(launcher, executable, cmd_line, ec))
  83. {
  84. init.on_error(launcher, executable, cmd_line, ec);
  85. }
  86. template<typename Launcher, typename Init>
  87. inline std::false_type probe_on_error(
  88. Launcher & launcher, Init && init, base && );
  89. template<typename Launcher, typename Init>
  90. inline auto probe_on_error(Launcher & launcher, Init && init, derived && )
  91. -> std::is_same<error_code, decltype(init.on_error(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>(), std::declval<error_code&>()))>;
  92. template<typename Launcher, typename Init>
  93. using has_on_error = decltype(probe_on_error(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
  94. template<typename Launcher>
  95. inline void on_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/,
  96. const error_code & /*ec*/)
  97. {
  98. }
  99. template<typename Launcher, typename Init1, typename ... Inits>
  100. inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  101. const error_code & ec,
  102. Init1 && init1,
  103. Inits && ... inits)
  104. {
  105. invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{});
  106. on_error(launcher, executable, cmd_line, ec, inits...);
  107. }
  108. template<typename Launcher, typename Init>
  109. inline void invoke_on_success(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*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, std::wstring &cmd_line,
  115. Init && init, derived && )
  116. -> decltype(init.on_success(launcher, executable, cmd_line))
  117. {
  118. init.on_success(launcher, executable, cmd_line);
  119. }
  120. template<typename Launcher, typename Init>
  121. inline std::false_type probe_on_success(
  122. Launcher & launcher, Init && init, base && );
  123. template<typename Launcher, typename Init>
  124. inline auto probe_on_success(Launcher & launcher, Init && init, derived && )
  125. -> std::is_same<error_code, decltype(init.on_success(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
  126. template<typename Launcher, typename Init>
  127. using has_on_success = decltype(probe_on_success(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
  128. template<typename Launcher>
  129. inline void on_success(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/)
  130. {
  131. }
  132. template<typename Launcher, typename Init1, typename ... Inits>
  133. inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
  134. Init1 && init1, Inits && ... inits)
  135. {
  136. invoke_on_success(launcher, executable, cmd_line, init1, derived{});
  137. on_success(launcher, executable, cmd_line, inits...);
  138. }
  139. template<typename Launcher, typename Init>
  140. struct is_initializer : std::integral_constant<bool,
  141. has_on_setup<Launcher, Init>::value ||
  142. has_on_error<Launcher, Init>::value ||
  143. has_on_success<Launcher, Init>::value>
  144. {
  145. };
  146. template<typename Launcher, typename ... Inits>
  147. struct all_are_initializers;
  148. template<typename Launcher>
  149. struct all_are_initializers<Launcher> : std::true_type {};
  150. template<typename Launcher, typename Init>
  151. struct all_are_initializers<Launcher, Init> : is_initializer<Launcher, Init> {};
  152. template<typename Launcher, typename Init, typename ... Tail>
  153. struct all_are_initializers<Launcher, Init, Tail...>
  154. : std::integral_constant<bool, is_initializer<Launcher, Init>::value && all_are_initializers<Launcher, Tail...>::value>
  155. {
  156. };
  157. }
  158. template<typename Executor>
  159. struct basic_process;
  160. namespace windows
  161. {
  162. /// The default launcher for processes on windows.
  163. struct default_launcher
  164. {
  165. //// The process_attributes passed to CreateProcess
  166. SECURITY_ATTRIBUTES * process_attributes = nullptr;
  167. //// The thread_attributes passed to CreateProcess
  168. SECURITY_ATTRIBUTES * thread_attributes = nullptr;
  169. /// The inhreited_handles option. bInheritHandles will be true if not empty..
  170. std::vector<HANDLE> inherited_handles;
  171. /// The creation flags of the process. Initializers may add to them; extended startupinfo is assumed.
  172. DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT};
  173. /// A pointer to the subprocess environment.
  174. void * environment = nullptr;
  175. /// The startup director. An empty path will get ignored.
  176. filesystem::path current_directory{};
  177. /// The full startup info passed to CreateProcess
  178. STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr,
  179. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr,
  180. INVALID_HANDLE_VALUE,
  181. INVALID_HANDLE_VALUE,
  182. INVALID_HANDLE_VALUE},
  183. nullptr};
  184. /// Allow batch files to be executed, which might pose a security threat.
  185. bool allow_batch_files = false;
  186. /// The process_information that gets assigned after a call to CreateProcess
  187. PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
  188. template<typename Executor, typename ... Inits>
  189. using enable_init = typename std::enable_if<
  190. detail::all_are_initializers<default_launcher, Inits...>::value,
  191. basic_process<Executor>>::type;
  192. default_launcher() = default;
  193. template<typename ExecutionContext, typename Args, typename ... Inits>
  194. auto operator()(ExecutionContext & context,
  195. const typename std::enable_if<std::is_convertible<
  196. ExecutionContext&, net::execution_context&>::value,
  197. filesystem::path >::type & executable,
  198. Args && args,
  199. Inits && ... inits ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
  200. {
  201. error_code ec;
  202. auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  203. if (ec)
  204. v2::detail::throw_error(ec, "default_launcher");
  205. return proc;
  206. }
  207. template<typename ExecutionContext, typename Args, typename ... Inits>
  208. auto operator()(ExecutionContext & context,
  209. error_code & ec,
  210. const typename std::enable_if<std::is_convertible<
  211. ExecutionContext&, net::execution_context&>::value,
  212. filesystem::path >::type & executable,
  213. Args && args,
  214. Inits && ... inits ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
  215. {
  216. return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  217. }
  218. template<typename Executor, typename Args, typename ... Inits>
  219. auto operator()(Executor exec,
  220. const typename std::enable_if<
  221. net::execution::is_executor<Executor>::value
  222. || net::is_executor<Executor>::value,
  223. filesystem::path >::type & executable,
  224. Args && args,
  225. Inits && ... inits ) -> enable_init<Executor, Inits...>
  226. {
  227. error_code ec;
  228. auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
  229. if (ec)
  230. detail::throw_error(ec, "default_launcher");
  231. return proc;
  232. }
  233. template<typename Executor, typename Args, typename ... Inits>
  234. auto operator()(Executor exec,
  235. error_code & ec,
  236. const typename std::enable_if<
  237. net::execution::is_executor<Executor>::value ||
  238. net::is_executor<Executor>::value,
  239. filesystem::path >::type & executable,
  240. Args && args,
  241. Inits && ... inits ) -> enable_init<Executor, Inits...>
  242. {
  243. if (!allow_batch_files && ((executable.extension() == ".bat") || (executable.extension() == ".cmd")))
  244. {
  245. BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_ACCESS_DENIED, system_category());
  246. return basic_process<Executor>(exec);
  247. }
  248. auto command_line = this->build_command_line(executable, std::forward<Args>(args));
  249. ec = detail::on_setup(*this, executable, command_line, inits...);
  250. if (ec)
  251. {
  252. detail::on_error(*this, executable, command_line, ec, inits...);
  253. return basic_process<Executor>(exec);
  254. }
  255. if (!inherited_handles.empty())
  256. {
  257. set_handle_list(ec);
  258. if (ec)
  259. return basic_process<Executor>(exec);
  260. }
  261. auto ok = ::CreateProcessW(
  262. executable.empty() ? nullptr : executable.c_str(),
  263. command_line.empty() ? nullptr : &command_line.front(),
  264. process_attributes,
  265. thread_attributes,
  266. inherited_handles.empty() ? FALSE : TRUE,
  267. creation_flags,
  268. environment,
  269. current_directory.empty() ? nullptr : current_directory.c_str(),
  270. &startup_info.StartupInfo,
  271. &process_information);
  272. if (ok == 0)
  273. {
  274. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
  275. detail::on_error(*this, executable, command_line, ec, inits...);
  276. if (process_information.hProcess != INVALID_HANDLE_VALUE)
  277. ::CloseHandle(process_information.hProcess);
  278. if (process_information.hThread != INVALID_HANDLE_VALUE)
  279. ::CloseHandle(process_information.hThread);
  280. return basic_process<Executor>(exec);
  281. }
  282. else
  283. {
  284. detail::on_success(*this, executable, command_line, inits...);
  285. if (process_information.hThread != INVALID_HANDLE_VALUE)
  286. ::CloseHandle(process_information.hThread);
  287. return basic_process<Executor>(exec,
  288. this->process_information.dwProcessId,
  289. this->process_information.hProcess);
  290. }
  291. }
  292. BOOST_PROCESS_V2_DECL static
  293. std::size_t escaped_argv_length(basic_string_view<wchar_t> ws);
  294. BOOST_PROCESS_V2_DECL static
  295. std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
  296. basic_string_view<wchar_t> ws);
  297. template<typename Argv>
  298. static std::wstring build_command_line_impl(
  299. const filesystem::path & pt,
  300. const Argv & argv,
  301. basic_string_view<wchar_t> args)
  302. {
  303. std::size_t req_size = std::accumulate(
  304. std::begin(argv), std::end(argv), escaped_argv_length(pt.native()),
  305. [](std::size_t sz, basic_string_view<wchar_t> arg) -> std::size_t
  306. {
  307. return sz + 1u + escaped_argv_length(arg);
  308. });
  309. std::wstring res;
  310. res.resize(req_size, L' ');
  311. wchar_t * itr = &res.front();
  312. itr += escape_argv_string(itr, res.size(), pt.native());
  313. for (const auto & a : argv)
  314. {
  315. itr++;
  316. itr += escape_argv_string(itr, std::distance(itr, &res.back() + 1), a);
  317. }
  318. return res;
  319. }
  320. template<typename Argv>
  321. static std::wstring build_command_line_impl(
  322. const filesystem::path & pt,
  323. const Argv & argv,
  324. basic_string_view<char> args)
  325. {
  326. std::vector<std::wstring> argw;
  327. argw.resize(std::distance(std::begin(argv), std::end(argv)));
  328. std::transform(std::begin(argv), std::end(argv), argw.begin(),
  329. [](basic_string_view <char> arg)
  330. {
  331. return detail::conv_string<wchar_t>(arg.data(), arg.size());
  332. });
  333. return build_command_line_impl(pt, argw, L"");
  334. }
  335. template<typename Args,
  336. typename Char = decltype(*std::begin(std::declval<Args>()))>
  337. static std::wstring build_command_line(const filesystem::path & pt, const Args & args)
  338. {
  339. if (std::begin(args) == std::end(args))
  340. {
  341. std::wstring buffer;
  342. buffer.resize(escaped_argv_length(pt.native()));
  343. if (!buffer.empty())
  344. escape_argv_string(&buffer.front(), buffer.size(), pt.native());
  345. return buffer;
  346. }
  347. return build_command_line_impl(pt, args, *std::begin(args));
  348. }
  349. static std::wstring build_command_line(const filesystem::path & pt, const wchar_t * args)
  350. {
  351. return args;
  352. }
  353. struct lpproc_thread_closer
  354. {
  355. void operator()(::LPPROC_THREAD_ATTRIBUTE_LIST l)
  356. {
  357. ::DeleteProcThreadAttributeList(l);
  358. ::HeapFree(GetProcessHeap(), 0, l);
  359. }
  360. };
  361. std::unique_ptr<std::remove_pointer<LPPROC_THREAD_ATTRIBUTE_LIST>::type, lpproc_thread_closer> proc_attribute_list_storage;
  362. BOOST_PROCESS_V2_DECL LPPROC_THREAD_ATTRIBUTE_LIST get_thread_attribute_list(error_code & ec);
  363. BOOST_PROCESS_V2_DECL void set_handle_list(error_code & ec);
  364. };
  365. }
  366. BOOST_PROCESS_V2_END_NAMESPACE
  367. #endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP