shell.hpp
4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// 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 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<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)),
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<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:
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;
int reserved_ = 0;
};
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
#include <boost/process/v2/impl/shell.ipp>
#endif
#endif //BOOST_PROCESS_V2_ERROR_HPP