// Copyright (c) 2022 Klemens D. Morgenstern // // 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_SHELL_HPP #define BOOST_PROCESS_V2_SHELL_HPP #include #include #include #include #include #include #include #include BOOST_PROCESS_V2_BEGIN_NAMESPACE /// Error category used by the shell parser. extern BOOST_PROCESS_V2_DECL const error_category& get_shell_category(); static const error_category& shell_category = get_shell_category(); /// Utility to parse commands /** This utility class parses command lines into tokens * and allows users to executed based on textual inputs. * * In v1, this was possible directly when starting a process, * but has been removed based on the security risks associated with this. * * By making the shell parsing explicity, it is encouraged * that a user runs a sanity check on the executable before launching it. * * @par Example * @code {.cpp} * asio::io_context ctx; * * auto cmd = shell("my-app --help"); * auto exe = cmd.exe(); * check_if_malicious(exe); * * process proc{ctx, exe, cmd.args()}; * * @endcode * * */ struct shell { #if defined(BOOST_PROCESS_V2_WINDOWS) using char_type = wchar_t; using args_type = const wchar_t *; #else using char_type = char; using args_type = const char **; #endif shell() = default; template shell(basic_string_view input) : buffer_(detail::conv_string(input.data(), input.size())) { parse_(); } shell(basic_cstring_ref input) : input_(input) {parse_();} shell(basic_string_view< typename std::conditional< std::is_same::value, wchar_t, char>::type> input) : buffer_(detail::conv_string(input.data(), input.size())) { parse_(); } shell(const shell &) = delete; shell& operator=(const shell &) = delete; shell(shell && lhs) noexcept : buffer_(std::move(lhs.buffer_)), input_(std::move(lhs.input_)), argc_(boost::exchange(lhs.argc_, 0)), argv_(boost::exchange(lhs.argv_, nullptr)), reserved_(boost::exchange(lhs.reserved_, 0)) { } shell& operator=(shell && lhs) noexcept { shell tmp(std::move(*this)); buffer_ = std::move(lhs.buffer_); input_ = std::move(lhs.input_); argc_ = boost::exchange(lhs.argc_, 0); argv_ = boost::exchange(lhs.argv_, nullptr); reserved_ = boost::exchange(lhs.reserved_, 0); return *this; } // the length of the parsed shell, including the executable int argc() const { return argc_; } char_type** argv() const { return argv_; } char_type** begin() const {return argv();} char_type** end() const {return argv() + argc();} bool empty() const {return argc() == 0;} std::size_t size() const {return static_cast(argc()); } /// Native representation of the arguments to be used - excluding the executable BOOST_PROCESS_V2_DECL args_type args() const; template filesystem::path exe(Environment && env = environment::current()) const { if (argc() == 0) return ""; else return environment::find_executable(0[argv()], std::forward(env)); } BOOST_PROCESS_V2_DECL ~shell(); private: BOOST_PROCESS_V2_DECL void parse_(); // storage in case we need a conversion std::basic_string buffer_; basic_cstring_ref input_{buffer_}; // impl details int argc_ = 0; char_type ** argv_ = nullptr; int reserved_ = 0; }; BOOST_PROCESS_V2_END_NAMESPACE #if defined(BOOST_PROCESS_V2_HEADER_ONLY) #include #endif #endif //BOOST_PROCESS_V2_ERROR_HPP