// // boost/process/v2/windows/default_launcher.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP #define BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP #include #include #include #include #include #include #include #include #include #include #if defined(BOOST_PROCESS_V2_STANDALONE) #include #include #include #else #include #include #include #endif BOOST_PROCESS_V2_BEGIN_NAMESPACE template struct basic_process; namespace detail { struct base {}; struct derived : base {}; template inline error_code invoke_on_setup(Launcher & /*launcher*/, const filesystem::path &executable, std::wstring &cmd_line, Init && /*init*/, base && ) { return error_code{}; } template inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init && init, derived && ) -> decltype(init.on_setup(launcher, executable, cmd_line)) { return init.on_setup(launcher, executable, cmd_line); } template inline std::false_type probe_on_setup( Launcher & launcher, Init && init, base && ); template inline auto probe_on_setup(Launcher & launcher, Init && init, derived && ) -> std::is_same(), std::declval()))>; template using has_on_setup = decltype(probe_on_setup(std::declval(), std::declval(), derived{})); template inline error_code on_setup(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/) { return error_code{}; } template inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init1 && init1, Inits && ... inits) { auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{}); if (ec) return ec; else return on_setup(launcher, executable, cmd_line, inits...); } template inline void invoke_on_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/, const error_code & /*ec*/, Init && /*init*/, base && ) { } template inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, const error_code & ec, Init && init, derived && ) -> decltype(init.on_error(launcher, executable, cmd_line, ec)) { init.on_error(launcher, executable, cmd_line, ec); } template inline std::false_type probe_on_error( Launcher & launcher, Init && init, base && ); template inline auto probe_on_error(Launcher & launcher, Init && init, derived && ) -> std::is_same(), std::declval(), std::declval()))>; template using has_on_error = decltype(probe_on_error(std::declval(), std::declval(), derived{})); template inline void on_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/, const error_code & /*ec*/) { } template inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, const error_code & ec, Init1 && init1, Inits && ... inits) { invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{}); on_error(launcher, executable, cmd_line, ec, inits...); } template inline void invoke_on_success(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/, Init && /*init*/, base && ) { } template inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init && init, derived && ) -> decltype(init.on_success(launcher, executable, cmd_line)) { init.on_success(launcher, executable, cmd_line); } template inline std::false_type probe_on_success( Launcher & launcher, Init && init, base && ); template inline auto probe_on_success(Launcher & launcher, Init && init, derived && ) -> std::is_same(), std::declval()))>; template using has_on_success = decltype(probe_on_success(std::declval(), std::declval(), derived{})); template inline void on_success(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/) { } template inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, Init1 && init1, Inits && ... inits) { invoke_on_success(launcher, executable, cmd_line, init1, derived{}); on_success(launcher, executable, cmd_line, inits...); } template struct is_initializer : std::integral_constant::value || has_on_error::value || has_on_success::value> { }; template struct all_are_initializers; template struct all_are_initializers : std::true_type {}; template struct all_are_initializers : is_initializer {}; template struct all_are_initializers : std::integral_constant::value && all_are_initializers::value> { }; } template struct basic_process; namespace windows { /// The default launcher for processes on windows. struct default_launcher { //// The process_attributes passed to CreateProcess SECURITY_ATTRIBUTES * process_attributes = nullptr; //// The thread_attributes passed to CreateProcess SECURITY_ATTRIBUTES * thread_attributes = nullptr; /// The inhreited_handles option. bInheritHandles will be true if not empty.. std::vector inherited_handles; /// The creation flags of the process. Initializers may add to them; extended startupinfo is assumed. DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT}; /// A pointer to the subprocess environment. void * environment = nullptr; /// The startup director. An empty path will get ignored. filesystem::path current_directory{}; /// The full startup info passed to CreateProcess STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, nullptr}; /// Allow batch files to be executed, which might pose a security threat. bool allow_batch_files = false; /// The process_information that gets assigned after a call to CreateProcess PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0}; template using enable_init = typename std::enable_if< detail::all_are_initializers::value, basic_process>::type; default_launcher() = default; template auto operator()(ExecutionContext & context, const typename std::enable_if::value, filesystem::path >::type & executable, Args && args, Inits && ... inits ) -> enable_init { error_code ec; auto proc = (*this)(context, ec, executable, std::forward(args), std::forward(inits)...); if (ec) v2::detail::throw_error(ec, "default_launcher"); return proc; } template auto operator()(ExecutionContext & context, error_code & ec, const typename std::enable_if::value, filesystem::path >::type & executable, Args && args, Inits && ... inits ) -> enable_init { return (*this)(context.get_executor(), ec, executable, std::forward(args), std::forward(inits)...); } template auto operator()(Executor exec, const typename std::enable_if< net::execution::is_executor::value || net::is_executor::value, filesystem::path >::type & executable, Args && args, Inits && ... inits ) -> enable_init { error_code ec; auto proc = (*this)(std::move(exec), ec, executable, std::forward(args), std::forward(inits)...); if (ec) detail::throw_error(ec, "default_launcher"); return proc; } template auto operator()(Executor exec, error_code & ec, const typename std::enable_if< net::execution::is_executor::value || net::is_executor::value, filesystem::path >::type & executable, Args && args, Inits && ... inits ) -> enable_init { if (!allow_batch_files && ((executable.extension() == ".bat") || (executable.extension() == ".cmd"))) { BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_ACCESS_DENIED, system_category()); return basic_process(exec); } auto command_line = this->build_command_line(executable, std::forward(args)); ec = detail::on_setup(*this, executable, command_line, inits...); if (ec) { detail::on_error(*this, executable, command_line, ec, inits...); return basic_process(exec); } if (!inherited_handles.empty()) { set_handle_list(ec); if (ec) return basic_process(exec); } auto ok = ::CreateProcessW( executable.empty() ? nullptr : executable.c_str(), command_line.empty() ? nullptr : &command_line.front(), process_attributes, thread_attributes, inherited_handles.empty() ? FALSE : TRUE, creation_flags, environment, current_directory.empty() ? nullptr : current_directory.c_str(), &startup_info.StartupInfo, &process_information); if (ok == 0) { BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); detail::on_error(*this, executable, command_line, ec, inits...); if (process_information.hProcess != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hProcess); if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec); } else { detail::on_success(*this, executable, command_line, inits...); if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec, this->process_information.dwProcessId, this->process_information.hProcess); } } BOOST_PROCESS_V2_DECL static std::size_t escaped_argv_length(basic_string_view ws); BOOST_PROCESS_V2_DECL static std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size, basic_string_view ws); template static std::wstring build_command_line_impl( const filesystem::path & pt, const Argv & argv, basic_string_view args) { std::size_t req_size = std::accumulate( std::begin(argv), std::end(argv), escaped_argv_length(pt.native()), [](std::size_t sz, basic_string_view arg) -> std::size_t { return sz + 1u + escaped_argv_length(arg); }); std::wstring res; res.resize(req_size, L' '); wchar_t * itr = &res.front(); itr += escape_argv_string(itr, res.size(), pt.native()); for (const auto & a : argv) { itr++; itr += escape_argv_string(itr, std::distance(itr, &res.back() + 1), a); } return res; } template static std::wstring build_command_line_impl( const filesystem::path & pt, const Argv & argv, basic_string_view args) { std::vector argw; argw.resize(std::distance(std::begin(argv), std::end(argv))); std::transform(std::begin(argv), std::end(argv), argw.begin(), [](basic_string_view arg) { return detail::conv_string(arg.data(), arg.size()); }); return build_command_line_impl(pt, argw, L""); } template()))> static std::wstring build_command_line(const filesystem::path & pt, const Args & args) { if (std::begin(args) == std::end(args)) { std::wstring buffer; buffer.resize(escaped_argv_length(pt.native())); if (!buffer.empty()) escape_argv_string(&buffer.front(), buffer.size(), pt.native()); return buffer; } return build_command_line_impl(pt, args, *std::begin(args)); } static std::wstring build_command_line(const filesystem::path & pt, const wchar_t * args) { return args; } struct lpproc_thread_closer { void operator()(::LPPROC_THREAD_ATTRIBUTE_LIST l) { ::DeleteProcThreadAttributeList(l); ::HeapFree(GetProcessHeap(), 0, l); } }; std::unique_ptr::type, lpproc_thread_closer> proc_attribute_list_storage; BOOST_PROCESS_V2_DECL LPPROC_THREAD_ATTRIBUTE_LIST get_thread_attribute_list(error_code & ec); BOOST_PROCESS_V2_DECL void set_handle_list(error_code & ec); }; } BOOST_PROCESS_V2_END_NAMESPACE #endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP