shell.hpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // Copyright (c) 2022 Klemens D. Morgenstern
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_PROCESS_V2_SHELL_HPP
  6. #define BOOST_PROCESS_V2_SHELL_HPP
  7. #include <boost/core/exchange.hpp>
  8. #include <boost/process/v2/cstring_ref.hpp>
  9. #include <boost/process/v2/detail/config.hpp>
  10. #include <boost/process/v2/detail/utf8.hpp>
  11. #include <boost/process/v2/detail/throw_error.hpp>
  12. #include <boost/process/v2/environment.hpp>
  13. #include <memory>
  14. #include <string>
  15. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  16. /// Error category used by the shell parser.
  17. extern BOOST_PROCESS_V2_DECL const error_category& get_shell_category();
  18. static const error_category& shell_category = get_shell_category();
  19. /// Utility to parse commands
  20. /** This utility class parses command lines into tokens
  21. * and allows users to executed based on textual inputs.
  22. *
  23. * In v1, this was possible directly when starting a process,
  24. * but has been removed based on the security risks associated with this.
  25. *
  26. * By making the shell parsing explicitly, it encourages
  27. * a user to run a sanity check on the executable before launching it.
  28. *
  29. * @par Example
  30. * @code {.cpp}
  31. * asio::io_context ctx;
  32. *
  33. * auto cmd = shell("my-app --help");
  34. * auto exe = cmd.exe();
  35. * check_if_malicious(exe);
  36. *
  37. * process proc{ctx, exe, cmd.args()};
  38. *
  39. * @endcode
  40. *
  41. *
  42. */
  43. struct shell
  44. {
  45. #if defined(BOOST_PROCESS_V2_WINDOWS)
  46. using char_type = wchar_t;
  47. using args_type = const wchar_t *;
  48. #else
  49. using char_type = char;
  50. using args_type = const char **;
  51. #endif
  52. shell() = default;
  53. template<typename Char, typename Traits>
  54. shell(basic_string_view<Char, Traits> input)
  55. : buffer_(detail::conv_string<char_type>(input.data(), input.size()))
  56. {
  57. parse_();
  58. }
  59. shell(basic_cstring_ref<char_type> input) : input_(input) {parse_();}
  60. shell(basic_string_view<
  61. typename std::conditional<
  62. std::is_same<char_type, char>::value,
  63. wchar_t, char>::type> input) : buffer_(detail::conv_string<char_type>(input.data(), input.size()))
  64. {
  65. parse_();
  66. }
  67. shell(const shell &) = delete;
  68. shell& operator=(const shell &) = delete;
  69. shell(shell && lhs) noexcept
  70. : buffer_(std::move(lhs.buffer_)),
  71. input_(std::move(lhs.input_)),
  72. argc_(boost::exchange(lhs.argc_, 0)),
  73. argv_(boost::exchange(lhs.argv_, nullptr))
  74. #if defined(BOOST_PROCESS_V2_POSIX)
  75. , free_argv_(boost::exchange(lhs.free_argv_, nullptr))
  76. #endif
  77. {
  78. }
  79. shell& operator=(shell && lhs) noexcept
  80. {
  81. shell tmp(std::move(*this));
  82. buffer_ = std::move(lhs.buffer_);
  83. input_ = std::move(lhs.input_);
  84. argc_ = boost::exchange(lhs.argc_, 0);
  85. argv_ = boost::exchange(lhs.argv_, nullptr);
  86. #if defined(BOOST_PROCESS_V2_POSIX)
  87. free_argv_ = boost::exchange(lhs.free_argv_, nullptr);
  88. #endif
  89. return *this;
  90. }
  91. // the length of the parsed shell, including the executable
  92. int argc() const { return argc_; }
  93. char_type** argv() const { return argv_; }
  94. char_type** begin() const {return argv();}
  95. char_type** end() const {return argv() + argc();}
  96. bool empty() const {return argc() == 0;}
  97. std::size_t size() const {return static_cast<std::size_t>(argc()); }
  98. /// Native representation of the arguments to be used - excluding the executable
  99. BOOST_PROCESS_V2_DECL args_type args() const;
  100. template<typename Environment = environment::current_view>
  101. filesystem::path exe(Environment && env = environment::current()) const
  102. {
  103. if (argc() == 0)
  104. return "";
  105. else
  106. return environment::find_executable(0[argv()], std::forward<Environment>(env));
  107. }
  108. BOOST_PROCESS_V2_DECL ~shell();
  109. private:
  110. friend struct make_cmd_shell_;
  111. BOOST_PROCESS_V2_DECL void parse_();
  112. // storage in case we need a conversion
  113. std::basic_string<char_type> buffer_;
  114. basic_cstring_ref<char_type> input_{buffer_};
  115. // impl details
  116. int argc_ = 0;
  117. char_type ** argv_ = nullptr;
  118. #if defined(BOOST_PROCESS_V2_POSIX)
  119. void(*free_argv_)(int, char **);
  120. #endif
  121. };
  122. BOOST_PROCESS_V2_END_NAMESPACE
  123. #endif //BOOST_PROCESS_V2_ERROR_HPP