144 lines
4.3 KiB
C++
144 lines
4.3 KiB
C++
// 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 <boost/core/exchange.hpp>
|
|
#include <boost/process/v2/cstring_ref.hpp>
|
|
#include <boost/process/v2/detail/config.hpp>
|
|
#include <boost/process/v2/detail/utf8.hpp>
|
|
#include <boost/process/v2/detail/throw_error.hpp>
|
|
#include <boost/process/v2/environment.hpp>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
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 explicitly, it encourages
|
|
* a user to run 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<typename Char, typename Traits>
|
|
shell(basic_string_view<Char, Traits> input)
|
|
: buffer_(detail::conv_string<char_type>(input.data(), input.size()))
|
|
{
|
|
parse_();
|
|
}
|
|
|
|
shell(basic_cstring_ref<char_type> input) : input_(input) {parse_();}
|
|
shell(basic_string_view<
|
|
typename std::conditional<
|
|
std::is_same<char_type, char>::value,
|
|
wchar_t, char>::type> input) : buffer_(detail::conv_string<char_type>(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))
|
|
#if defined(BOOST_PROCESS_V2_POSIX)
|
|
, free_argv_(boost::exchange(lhs.free_argv_, nullptr))
|
|
#endif
|
|
{
|
|
}
|
|
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);
|
|
#if defined(BOOST_PROCESS_V2_POSIX)
|
|
free_argv_ = boost::exchange(lhs.free_argv_, nullptr);
|
|
#endif
|
|
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<std::size_t>(argc()); }
|
|
/// Native representation of the arguments to be used - excluding the executable
|
|
BOOST_PROCESS_V2_DECL args_type args() const;
|
|
template<typename Environment = environment::current_view>
|
|
filesystem::path exe(Environment && env = environment::current()) const
|
|
{
|
|
if (argc() == 0)
|
|
return "";
|
|
else
|
|
return environment::find_executable(0[argv()], std::forward<Environment>(env));
|
|
}
|
|
BOOST_PROCESS_V2_DECL ~shell();
|
|
|
|
private:
|
|
|
|
friend struct make_cmd_shell_;
|
|
|
|
BOOST_PROCESS_V2_DECL void parse_();
|
|
|
|
// storage in case we need a conversion
|
|
std::basic_string<char_type> buffer_;
|
|
basic_cstring_ref<char_type> input_{buffer_};
|
|
// impl details
|
|
int argc_ = 0;
|
|
char_type ** argv_ = nullptr;
|
|
|
|
#if defined(BOOST_PROCESS_V2_POSIX)
|
|
void(*free_argv_)(int, char **);
|
|
#endif
|
|
|
|
};
|
|
|
|
BOOST_PROCESS_V2_END_NAMESPACE
|
|
|
|
#endif //BOOST_PROCESS_V2_ERROR_HPP
|