mirror of
https://github.com/boostorg/process.git
synced 2026-01-21 05:02:16 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89c5b589c3 | ||
|
|
4183c66490 | ||
|
|
9cdc1be868 | ||
|
|
b9cb5dc0ee | ||
|
|
4cc469b2a4 | ||
|
|
6e4d1e29d2 | ||
|
|
dada865fd0 | ||
|
|
380dd1b00f | ||
|
|
7832cb6af3 | ||
|
|
68f4c50be9 | ||
|
|
cd226a7616 | ||
|
|
4243ce72f8 | ||
|
|
9065833e61 | ||
|
|
7cb7af6c8b | ||
|
|
90cbe7cec0 | ||
|
|
df33c1ad7b | ||
|
|
bbabea30dd | ||
|
|
8a61f8daa3 |
@@ -139,7 +139,7 @@ struct exe_cmd_init<char> : boost::process::detail::api::handler_base_ext
|
|||||||
}
|
}
|
||||||
static exe_cmd_init cmd_shell(std::string&& cmd)
|
static exe_cmd_init cmd_shell(std::string&& cmd)
|
||||||
{
|
{
|
||||||
std::vector<std::string> args = {"-c", "\"" + cmd + "\""};
|
std::vector<std::string> args = {"-c", cmd};
|
||||||
std::string sh = shell().string();
|
std::string sh = shell().string();
|
||||||
|
|
||||||
return exe_cmd_init(
|
return exe_cmd_init(
|
||||||
|
|||||||
@@ -155,8 +155,8 @@ class executor
|
|||||||
void write_error(const std::error_code & ec, const char * msg)
|
void write_error(const std::error_code & ec, const char * msg)
|
||||||
{
|
{
|
||||||
//I am the child
|
//I am the child
|
||||||
const auto len = std::strlen(msg);
|
const auto len = static_cast<int>(std::strlen(msg));
|
||||||
int data[2] = {ec.value(), static_cast<int>(len + 1)};
|
int data[2] = {ec.value(), len + 1};
|
||||||
|
|
||||||
boost::ignore_unused(::write(_pipe_sink, &data[0], sizeof(int) * 2));
|
boost::ignore_unused(::write(_pipe_sink, &data[0], sizeof(int) * 2));
|
||||||
boost::ignore_unused(::write(_pipe_sink, msg, len));
|
boost::ignore_unused(::write(_pipe_sink, msg, len));
|
||||||
@@ -444,6 +444,8 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
|
|||||||
}
|
}
|
||||||
if (_ec)
|
if (_ec)
|
||||||
{
|
{
|
||||||
|
//if an error occured we need to reap the child process
|
||||||
|
::waitpid(this->pid, nullptr, WNOHANG);
|
||||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||||
return child();
|
return child();
|
||||||
}
|
}
|
||||||
@@ -537,6 +539,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
|
|||||||
|
|
||||||
if (_ec)
|
if (_ec)
|
||||||
{
|
{
|
||||||
|
::waitpid(this->pid, nullptr, WNOHANG);
|
||||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||||
return child();
|
return child();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ public:
|
|||||||
auto st1 = key + ::boost::process::detail::equal_sign<Char>();
|
auto st1 = key + ::boost::process::detail::equal_sign<Char>();
|
||||||
while (*p != nullptr)
|
while (*p != nullptr)
|
||||||
{
|
{
|
||||||
const int len = std::char_traits<Char>::length(*p);
|
const auto len = std::char_traits<Char>::length(*p);
|
||||||
if ((std::distance(st1.begin(), st1.end()) < len)
|
if ((std::distance(st1.begin(), st1.end()) < len)
|
||||||
&& std::equal(st1.begin(), st1.end(), *p))
|
&& std::equal(st1.begin(), st1.end(), *p))
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -12,6 +12,16 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
#if !defined(BOOST_PROCESS_V2_DISABLE_UNDOCUMENTED_API)
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
|
||||||
|
LONG WINAPI NtResumeProcess(HANDLE ProcessHandle);
|
||||||
|
LONG WINAPI NtSuspendProcess(HANDLE ProcessHandle);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -77,14 +87,14 @@ static BOOL CALLBACK enum_window(HWND hwnd, LPARAM param)
|
|||||||
auto data = reinterpret_cast<enum_windows_data_t*>(param);
|
auto data = reinterpret_cast<enum_windows_data_t*>(param);
|
||||||
DWORD pid{0u};
|
DWORD pid{0u};
|
||||||
GetWindowThreadProcessId(hwnd, &pid);
|
GetWindowThreadProcessId(hwnd, &pid);
|
||||||
|
|
||||||
if (pid != data->pid)
|
if (pid != data->pid)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
LRESULT res = ::SendMessageW(hwnd, WM_CLOSE, 0, 0);
|
LRESULT res = ::SendMessageW(hwnd, WM_CLOSE, 0, 0);
|
||||||
if (!res)
|
|
||||||
|
if (res)
|
||||||
data->ec = detail::get_last_error();
|
data->ec = detail::get_last_error();
|
||||||
return res != 0;
|
return res == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void request_exit_(pid_type pid_, error_code & ec)
|
void request_exit_(pid_type pid_, error_code & ec)
|
||||||
@@ -113,6 +123,33 @@ void check_running_(HANDLE handle, error_code & ec, DWORD & exit_status)
|
|||||||
ec = detail::get_last_error();
|
ec = detail::get_last_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(BOOST_PROCESS_V2_DISABLE_UNDOCUMENTED_API)
|
||||||
|
void suspend_(HANDLE handle, error_code & ec)
|
||||||
|
{
|
||||||
|
auto nt_err = NtSuspendProcess(handle);
|
||||||
|
if (nt_err > 0xC0000000)
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume_(HANDLE handle, error_code & ec)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto nt_err = NtResumeProcess(handle);
|
||||||
|
if (nt_err > 0xC0000000)
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void suspend_(HANDLE, error_code & ec)
|
||||||
|
{
|
||||||
|
ec.assign(ERROR_CALL_NOT_IMPLEMENTED, system_category());
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume_(HANDLE handle, error_code & ec)
|
||||||
|
{
|
||||||
|
|
||||||
|
ec.assign(ERROR_CALL_NOT_IMPLEMENTED, system_category());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||||
template struct basic_process_handle_win<>;
|
template struct basic_process_handle_win<>;
|
||||||
|
|||||||
@@ -182,7 +182,42 @@ struct basic_process_handle_fd
|
|||||||
if (ec)
|
if (ec)
|
||||||
detail::throw_error(ec, "request_exit");
|
detail::throw_error(ec, "request_exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void suspend()
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
error_code ec;
|
||||||
|
suspend(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "suspend");
|
||||||
|
}
|
||||||
|
|
||||||
|
void suspend(error_code &ec)
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
if (::kill(pid_, SIGCONT) == -1)
|
||||||
|
ec = get_last_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume()
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
error_code ec;
|
||||||
|
resume(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "resume");
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume(error_code &ec)
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
if (::kill(pid_, SIGTERM) == -1)
|
||||||
|
ec = get_last_error();
|
||||||
|
}
|
||||||
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
||||||
{
|
{
|
||||||
if (pid_ <= 0)
|
if (pid_ <= 0)
|
||||||
|
|||||||
@@ -211,6 +211,42 @@ struct basic_process_handle_fd_or_signal
|
|||||||
if (ec)
|
if (ec)
|
||||||
detail::throw_error(ec, "request_exit");
|
detail::throw_error(ec, "request_exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void suspend()
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
error_code ec;
|
||||||
|
suspend(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "suspend");
|
||||||
|
}
|
||||||
|
|
||||||
|
void suspend(error_code &ec)
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
if (::kill(pid_, SIGCONT) == -1)
|
||||||
|
ec = get_last_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume()
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
error_code ec;
|
||||||
|
resume(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "resume");
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume(error_code &ec)
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
if (::kill(pid_, SIGTERM) == -1)
|
||||||
|
ec = get_last_error();
|
||||||
|
}
|
||||||
|
|
||||||
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -180,6 +180,50 @@ struct basic_process_handle_signal
|
|||||||
detail::throw_error(ec, "request_exit");
|
detail::throw_error(ec, "request_exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void interrupt(error_code &ec)
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
if (::kill(pid_, SIGTERM) == -1)
|
||||||
|
ec = get_last_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
void suspend()
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
error_code ec;
|
||||||
|
suspend(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "suspend");
|
||||||
|
}
|
||||||
|
|
||||||
|
void suspend(error_code &ec)
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
if (::kill(pid_, SIGCONT) == -1)
|
||||||
|
ec = get_last_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume()
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
error_code ec;
|
||||||
|
resume(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "resume");
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume(error_code &ec)
|
||||||
|
{
|
||||||
|
if (pid_ <= 0)
|
||||||
|
return ;
|
||||||
|
if (::kill(pid_, SIGTERM) == -1)
|
||||||
|
ec = get_last_error();
|
||||||
|
}
|
||||||
|
|
||||||
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
||||||
{
|
{
|
||||||
if (pid_ <= 0)
|
if (pid_ <= 0)
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ BOOST_PROCESS_V2_DECL void terminate_if_running_(void * handle);
|
|||||||
BOOST_PROCESS_V2_DECL bool check_handle_(void* handle, error_code & ec);
|
BOOST_PROCESS_V2_DECL bool check_handle_(void* handle, error_code & ec);
|
||||||
BOOST_PROCESS_V2_DECL bool check_pid_(pid_type pid_, error_code & ec);
|
BOOST_PROCESS_V2_DECL bool check_pid_(pid_type pid_, error_code & ec);
|
||||||
BOOST_PROCESS_V2_DECL void interrupt_(pid_type pid_, error_code & ec);
|
BOOST_PROCESS_V2_DECL void interrupt_(pid_type pid_, error_code & ec);
|
||||||
|
BOOST_PROCESS_V2_DECL void suspend_(void * handle, error_code & ec);
|
||||||
|
BOOST_PROCESS_V2_DECL void resume_(void * handle, error_code & ec);
|
||||||
BOOST_PROCESS_V2_DECL void terminate_(void * handle, error_code & ec, native_exit_code_type & exit_code);
|
BOOST_PROCESS_V2_DECL void terminate_(void * handle, error_code & ec, native_exit_code_type & exit_code);
|
||||||
BOOST_PROCESS_V2_DECL void request_exit_(pid_type pid_, error_code & ec);
|
BOOST_PROCESS_V2_DECL void request_exit_(pid_type pid_, error_code & ec);
|
||||||
BOOST_PROCESS_V2_DECL void check_running_(void* handle, error_code & ec, native_exit_code_type & exit_status);
|
BOOST_PROCESS_V2_DECL void check_running_(void* handle, error_code & ec, native_exit_code_type & exit_status);
|
||||||
@@ -87,17 +89,16 @@ struct basic_process_handle_win
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_process_handle_win(basic_process_handle_win && handle)
|
basic_process_handle_win(basic_process_handle_win && handle)
|
||||||
|
: pid_(handle.id()), handle_(std::move(handle.handle_))
|
||||||
{
|
{
|
||||||
pid_ = handle.id();
|
|
||||||
handle_ = std::move(handle.handle_);
|
|
||||||
handle.pid_ = static_cast<DWORD>(-1);
|
handle.pid_ = static_cast<DWORD>(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_process_handle_win& operator=(basic_process_handle_win && handle)
|
basic_process_handle_win& operator=(basic_process_handle_win && handle)
|
||||||
{
|
{
|
||||||
pid_ = handle.pid_;
|
pid_ = handle.pid_;
|
||||||
handle_ = std::mopve(handle_))
|
handle_ = std::move(handle.handle_);
|
||||||
handle.pid_ = static_cast<DWORD>(-1);
|
handle.pid_ = static_cast<DWORD>(-1);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -166,7 +167,6 @@ struct basic_process_handle_win
|
|||||||
{
|
{
|
||||||
if (!detail::check_pid_(pid_, ec))
|
if (!detail::check_pid_(pid_, ec))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
detail::request_exit_(pid_, ec);
|
detail::request_exit_(pid_, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +178,32 @@ struct basic_process_handle_win
|
|||||||
detail::throw_error(ec, "request_exit");
|
detail::throw_error(ec, "request_exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void suspend(error_code &ec)
|
||||||
|
{
|
||||||
|
detail::suspend_(handle_.native_handle(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void suspend()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
suspend(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "suspend");
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume(error_code &ec)
|
||||||
|
{
|
||||||
|
detail::resume_(handle_.native_handle(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
suspend(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "resume");
|
||||||
|
}
|
||||||
|
|
||||||
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
||||||
{
|
{
|
||||||
if (!detail::check_handle_(handle_.native_handle(), ec))
|
if (!detail::check_handle_(handle_.native_handle(), ec))
|
||||||
|
|||||||
@@ -979,7 +979,8 @@ struct key_value_pair
|
|||||||
const std::pair<Key, Value> & kv/*,
|
const std::pair<Key, Value> & kv/*,
|
||||||
typename std::enable_if<std::is_constructible<struct key, Key >::value &&
|
typename std::enable_if<std::is_constructible<struct key, Key >::value &&
|
||||||
std::is_constructible<struct value, Value>::value
|
std::is_constructible<struct value, Value>::value
|
||||||
>::type = 0*/) : value_(((struct key)(kv.first)).string() + equality_sign + ((struct value)(kv.second)).string())
|
>::type = 0*/) : value_(((struct key)(kv.first)).basic_string<char_type, traits_type>() + equality_sign
|
||||||
|
+ ((struct value)(kv.second)).basic_string<char_type, traits_type>())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
key_value_pair(const typename conditional<is_same<value_type, char>::value, wchar_t, char>::type * raw)
|
key_value_pair(const typename conditional<is_same<value_type, char>::value, wchar_t, char>::type * raw)
|
||||||
@@ -1045,6 +1046,7 @@ struct key_value_pair
|
|||||||
|
|
||||||
operator string_type() const {return native();}
|
operator string_type() const {return native();}
|
||||||
operator string_view_type() const {return native_view();}
|
operator string_view_type() const {return native_view();}
|
||||||
|
operator typename string_view_type::string_view_type() const {return native_view();}
|
||||||
operator key_value_pair_view() const {return native_view();}
|
operator key_value_pair_view() const {return native_view();}
|
||||||
|
|
||||||
int compare( const key_value_pair& p ) const noexcept
|
int compare( const key_value_pair& p ) const noexcept
|
||||||
@@ -1432,8 +1434,9 @@ auto find_key(Environment & env, key_view ky)
|
|||||||
template<typename Environment = current_view>
|
template<typename Environment = current_view>
|
||||||
inline filesystem::path home(Environment && env = current())
|
inline filesystem::path home(Environment && env = current())
|
||||||
{
|
{
|
||||||
#if defined(ASIO_WINDOWS)
|
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
return detail::find_key(env, L"HOMEDRIVE") + detail::find_key(env, L"HOMEPATH").native_string();
|
return detail::find_key(env, L"HOMEDRIVE").native_string()
|
||||||
|
+ detail::find_key(env, L"HOMEPATH").native_string();
|
||||||
#else
|
#else
|
||||||
return detail::find_key(env, "HOME").native_string();
|
return detail::find_key(env, "HOME").native_string();
|
||||||
#endif
|
#endif
|
||||||
@@ -1468,7 +1471,7 @@ inline BOOST_PROCESS_V2_NAMESPACE::filesystem::path find_executable(
|
|||||||
// first check if it has the extension already
|
// first check if it has the extension already
|
||||||
BOOST_PROCESS_V2_NAMESPACE::filesystem::path full_nm(name);
|
BOOST_PROCESS_V2_NAMESPACE::filesystem::path full_nm(name);
|
||||||
BOOST_PROCESS_V2_NAMESPACE::filesystem::path pp(pp_view.begin(), pp_view.end());
|
BOOST_PROCESS_V2_NAMESPACE::filesystem::path pp(pp_view.begin(), pp_view.end());
|
||||||
auto p = pp / nm;
|
auto p = pp / full_nm;
|
||||||
error_code ec;
|
error_code ec;
|
||||||
|
|
||||||
if (detail::is_executable(p, ec) && !ec)
|
if (detail::is_executable(p, ec) && !ec)
|
||||||
@@ -1695,67 +1698,52 @@ struct process_environment
|
|||||||
|
|
||||||
|
|
||||||
template<typename Args>
|
template<typename Args>
|
||||||
void build_env(Args && args, string_view rs)
|
static
|
||||||
|
std::vector<wchar_t> build_env(Args && args,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_convertible<
|
||||||
|
decltype(*std::begin(std::declval<Args>())),
|
||||||
|
wcstring_ref>::value>::type * = nullptr)
|
||||||
{
|
{
|
||||||
std::size_t length = 0u;
|
std::vector<wchar_t> res;
|
||||||
for (string_view v : args)
|
std::size_t sz = 1;
|
||||||
length += detail::size_as_wide(v.data(), v.size(), ec) + 1u;
|
for (wcstring_ref cs : std::forward<Args>(args))
|
||||||
|
sz =+ cs.size() + 1;
|
||||||
|
res.reserve(sz);
|
||||||
|
|
||||||
|
for (wcstring_ref cs : std::forward<Args>(args))
|
||||||
|
res.insert(res.end(), cs.begin(), std::next(cs.end()));
|
||||||
|
|
||||||
|
|
||||||
if (ec)
|
res.push_back(L'\0');
|
||||||
return;
|
return res;
|
||||||
length ++ ;
|
|
||||||
|
|
||||||
unicode_env.resize(length);
|
|
||||||
|
|
||||||
auto itr = &unicode_env.front();
|
|
||||||
for (string_view v : args)
|
|
||||||
{
|
|
||||||
itr += detail::convert_to_wide(
|
|
||||||
v.data(), v.size(),
|
|
||||||
itr, &unicode_env.back() - itr,
|
|
||||||
ec);
|
|
||||||
if (ec)
|
|
||||||
break;
|
|
||||||
*(itr++) = '\0';
|
|
||||||
}
|
|
||||||
unicode_env.back() = '\0';
|
|
||||||
}
|
|
||||||
template<typename Args>
|
|
||||||
void build_env(Args && args, wstring_view rs)
|
|
||||||
{
|
|
||||||
std::size_t length = 0u;
|
|
||||||
for (const auto & v : std::forward<Args>(args))
|
|
||||||
length += v.size() + 1u;
|
|
||||||
|
|
||||||
length ++ ;
|
|
||||||
|
|
||||||
unicode_env.resize(length);
|
|
||||||
|
|
||||||
auto itr = unicode_env.begin();
|
|
||||||
for (wstring_view v : args )
|
|
||||||
{
|
|
||||||
itr = std::copy(v.begin(), v.end(), itr);
|
|
||||||
*(itr++) = L'\0';
|
|
||||||
}
|
|
||||||
unicode_env.back() = L'\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Args>
|
||||||
|
std::vector<wchar_t> build_env(Args && args,
|
||||||
|
typename std::enable_if<
|
||||||
|
!std::is_convertible<
|
||||||
|
decltype(*std::begin(std::declval<Args>())),
|
||||||
|
wcstring_ref>::value>::type * = nullptr)
|
||||||
|
{
|
||||||
|
for (auto && arg: std::forward<Args>(args))
|
||||||
|
env_buffer.emplace_back(arg);
|
||||||
|
return build_env(env_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
process_environment(std::initializer_list<string_view> sv) { build_env(sv, ""); }
|
process_environment(std::initializer_list<string_view> sv) : unicode_env{build_env(sv, "")} {}
|
||||||
process_environment(std::initializer_list<wstring_view> sv) { build_env(sv, L""); }
|
process_environment(std::initializer_list<wstring_view> sv) : unicode_env{build_env(sv, L"")} {}
|
||||||
|
|
||||||
template<typename Args>
|
template<typename Args>
|
||||||
process_environment(Args && args)
|
process_environment(Args && args) : unicode_env{build_env(std::forward<Args>(args))}
|
||||||
{
|
{
|
||||||
if (std::begin(args) != std::end(args))
|
|
||||||
build_env(std::forward<Args>(args), *std::begin(args));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error_code error() {return ec;}
|
error_code error() {return ec;}
|
||||||
error_code ec;
|
error_code ec;
|
||||||
|
std::vector<environment::key_value_pair> env_buffer;
|
||||||
std::vector<wchar_t> unicode_env;
|
std::vector<wchar_t> unicode_env;
|
||||||
|
|
||||||
|
|
||||||
error_code on_setup(windows::default_launcher & launcher,
|
error_code on_setup(windows::default_launcher & launcher,
|
||||||
const filesystem::path &, const std::wstring &);
|
const filesystem::path &, const std::wstring &);
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,21 @@
|
|||||||
#define BOOST_PROCESS_V2_IMPL_PID_IPP
|
#define BOOST_PROCESS_V2_IMPL_PID_IPP
|
||||||
|
|
||||||
#include <boost/process/v2/detail/config.hpp>
|
#include <boost/process/v2/detail/config.hpp>
|
||||||
|
#include <boost/process/v2/detail/last_error.hpp>
|
||||||
|
#include <boost/process/v2/detail/throw_error.hpp>
|
||||||
#include <boost/process/v2/pid.hpp>
|
#include <boost/process/v2/pid.hpp>
|
||||||
|
|
||||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <tlhelp32.h>
|
||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if (defined(__APPLE__) && defined(__MACH__))
|
||||||
|
#include <sys/proc_info.h>
|
||||||
|
#include <libproc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||||
@@ -22,6 +31,220 @@ pid_type current_pid() {return ::GetCurrentProcessId();}
|
|||||||
pid_type current_pid() {return ::getpid();}
|
pid_type current_pid() {return ::getpid();}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
|
std::vector<pid_type> all_pids(error_code & ec)
|
||||||
|
{
|
||||||
|
std::vector<pid_type> vec;
|
||||||
|
HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
if (!hp)
|
||||||
|
{
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESSENTRY32 pe;
|
||||||
|
pe.dwSize = sizeof(PROCESSENTRY32);
|
||||||
|
if (Process32First(hp, &pe)) {
|
||||||
|
do {
|
||||||
|
vec.push_back(pe.th32ProcessID);
|
||||||
|
} while (Process32Next(hp, &pe));
|
||||||
|
}
|
||||||
|
CloseHandle(hp);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif (defined(__APPLE__) && defined(__MACH__))
|
||||||
|
|
||||||
|
std::vector<pid_type> all_pids(error_code & ec)
|
||||||
|
{
|
||||||
|
std::vector<pid_type> vec;
|
||||||
|
vec.resize(proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0));
|
||||||
|
|
||||||
|
if (proc_listpids(PROC_ALL_PIDS, 0, &vec[0], sizeof(pid_type) * vec))
|
||||||
|
{
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto itr = std::partition(vec.begin(), vec.end(), [](pid_type pt) {return pt != 0;});
|
||||||
|
vec.erase(itr, vec.end());
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
|
||||||
|
std::vector<pid_type> all_pids(error_code & ec)
|
||||||
|
{
|
||||||
|
std::vector<pid_type> vec;
|
||||||
|
DIR *proc = opendir("/proc");
|
||||||
|
if (proc == nullptr)
|
||||||
|
{
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
struct dirent *ent = nullptr;
|
||||||
|
while ((ent = readdir(proc) != nullptr))
|
||||||
|
{
|
||||||
|
if (!isdigit(*ent->d_name))
|
||||||
|
continue;
|
||||||
|
vec.push_back(atoi(ent->d_name));
|
||||||
|
}
|
||||||
|
closedir(proc);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
|
||||||
|
std::vector<pid_type> all_pids(error_code & ec)
|
||||||
|
{
|
||||||
|
std::vector<pid_type> vec;
|
||||||
|
int cntp = 0;
|
||||||
|
kinfo_proc *proc_info = kinfo_getallproc(&cntp);
|
||||||
|
if (proc_info)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cntp; i++)
|
||||||
|
vec.push_back(proc_info[i].ki_pid);
|
||||||
|
free(proc_info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__DragonFly__)
|
||||||
|
|
||||||
|
std::vector<pid_type> all_pids(error_code & ec)
|
||||||
|
{
|
||||||
|
std::vector<pid_type> vec;
|
||||||
|
int cntp = 0;
|
||||||
|
kinfo_proc *proc_info = nullptr;
|
||||||
|
const char *nlistf, *memf;
|
||||||
|
nlistf = memf = "/dev/null";
|
||||||
|
kd = kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr);
|
||||||
|
if (!kd)
|
||||||
|
{
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((proc_info = kvm_getprocs(kd, KERN_PROC_ALL, 0, &cntp)))
|
||||||
|
{
|
||||||
|
vec.reserve(cntp);
|
||||||
|
for (int i = 0; i < cntp; i++)
|
||||||
|
if (proc_info[i].kp_pid >= 0)
|
||||||
|
vec.push_back(proc_info[i].kp_pid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
|
||||||
|
kvm_close(kd);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
|
||||||
|
std::vector<pid_type> all_pids(error_code & ec)
|
||||||
|
{
|
||||||
|
std::vector<pid_type> vec;
|
||||||
|
int cntp = 0;
|
||||||
|
kinfo_proc2 *proc_info = nullptr;
|
||||||
|
kd = kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
|
||||||
|
if (!kd)
|
||||||
|
{
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
if ((proc_info = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &cntp)))
|
||||||
|
{
|
||||||
|
vec.reserve(cntp);
|
||||||
|
for (int i = cntp - 1; i >= 0; i--) {
|
||||||
|
vec.push_back(proc_info[i].p_pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
kvm_close(kd);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
|
||||||
|
std::vector<pid_type> all_pids(error_code & ec)
|
||||||
|
{
|
||||||
|
std::vector<pid_type> vec;
|
||||||
|
int cntp = 0;
|
||||||
|
kinfo_proc *proc_info = nullptr;
|
||||||
|
kd = kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
|
||||||
|
|
||||||
|
if (!kd)
|
||||||
|
{
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
if ((proc_info = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cntp)))
|
||||||
|
{
|
||||||
|
vec.reserve(cntp);
|
||||||
|
for (int i = 0; i < cntp; i++) {
|
||||||
|
if (proc_info[i].kp_pid >= 0) {
|
||||||
|
vec.push_back(proc_info[i].kp_pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
kvm_close(kd);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__sun)
|
||||||
|
|
||||||
|
std::vector<pid_type> all_pids(error_code & ec)
|
||||||
|
{
|
||||||
|
std::vector<pid_type> vec;
|
||||||
|
struct pid cur_pid;
|
||||||
|
proc *proc_info = nullptr;
|
||||||
|
kd = kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr);
|
||||||
|
if (!kd)
|
||||||
|
{
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((proc_info = kvm_nextproc(kd)))
|
||||||
|
{
|
||||||
|
if (kvm_kread(kd, (std::uintptr_t)proc_info->p_pidp, &cur_pid, sizeof(cur_pid)) != -1) {
|
||||||
|
vec.insert(vec.begin(), cur_pid.pid_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ec = detail::get_last_error();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kvm_close(kd);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Platform not supported"
|
||||||
|
|
||||||
|
|
||||||
|
#endif defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<pid_type> all_pids()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
auto res = all_pids(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "all_pids");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BOOST_PROCESS_V2_END_NAMESPACE
|
BOOST_PROCESS_V2_END_NAMESPACE
|
||||||
|
|
||||||
#endif //BOOST_PROCESS_V2_IMPL_PID_IPP
|
#endif //BOOST_PROCESS_V2_IMPL_PID_IPP
|
||||||
|
|||||||
131
include/boost/process/v2/impl/shell.ipp
Normal file
131
include/boost/process/v2/impl/shell.ipp
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
// 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_IMPL_SHELL_IPP
|
||||||
|
#define BOOST_PROCESS_V2_IMPL_SHELL_IPP
|
||||||
|
|
||||||
|
#include <boost/process/v2/detail/config.hpp>
|
||||||
|
#include <boost/process/v2/detail/last_error.hpp>
|
||||||
|
#include <boost/process/v2/detail/throw_error.hpp>
|
||||||
|
#include <boost/process/v2/detail/config.hpp>
|
||||||
|
#include <boost/process/v2/error.hpp>
|
||||||
|
#include <boost/process/v2/shell.hpp>
|
||||||
|
|
||||||
|
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
|
#include <shellapi.h>
|
||||||
|
#else
|
||||||
|
#include <wordexp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
|
BOOST_PROCESS_V2_DECL const error_category& get_shell_category()
|
||||||
|
{
|
||||||
|
return system_category();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct shell_category_t final : public error_category
|
||||||
|
{
|
||||||
|
shell_category_t() : error_category(0xDAF1u) {}
|
||||||
|
|
||||||
|
const char* name() const noexcept
|
||||||
|
{
|
||||||
|
return "process.v2.utf8";
|
||||||
|
}
|
||||||
|
std::string message(int value) const
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case WRDE_BADCHAR:
|
||||||
|
return "Illegal occurrence of newline or one of |, &, ;, <, >, (, ), {, }.";
|
||||||
|
case WRDE_BADVAL:
|
||||||
|
return "An undefined shell variable was referenced, and the WRDE_UNDEF flag told us to consider this an error.";
|
||||||
|
case WRDE_CMDSUB:
|
||||||
|
return "Command substitution occurred, and the WRDE_NOCMD flag told us to consider this an error.";
|
||||||
|
case WRDE_NOSPACE:
|
||||||
|
return "Out of memory.";
|
||||||
|
case WRDE_SYNTAX:
|
||||||
|
return "Shell syntax error, such as unbalanced parentheses or unmatched quotes.";
|
||||||
|
default:
|
||||||
|
return "process.v2.wordexp error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOST_PROCESS_V2_DECL const error_category& get_shell_category()
|
||||||
|
{
|
||||||
|
static shell_category_t instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined (BOOST_PROCESS_V2_WINDOWS)
|
||||||
|
|
||||||
|
void shell::parse_()
|
||||||
|
{
|
||||||
|
argv_ = ::CommandLineToArgvW(input_.c_str(), &argc_);
|
||||||
|
if (argv_ == nullptr)
|
||||||
|
detail::throw_last_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
shell::~shell()
|
||||||
|
{
|
||||||
|
if (argv_ != nullptr)
|
||||||
|
LocalFree(argv_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto shell::args() const-> args_type
|
||||||
|
{
|
||||||
|
return input_.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void shell::parse_()
|
||||||
|
{
|
||||||
|
wordexp_t we{};
|
||||||
|
auto cd = wordexp(input_.c_str(), &we, WRDE_NOCMD);
|
||||||
|
|
||||||
|
if (cd != 0)
|
||||||
|
detail::throw_error(error_code(cd, get_shell_category()), "shell::parse");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
argc_ = static_cast<int>(we.we_wordc);
|
||||||
|
argv_ = we.we_wordv;
|
||||||
|
reserved_ = static_cast<int>(we.we_offs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shell::~shell()
|
||||||
|
{
|
||||||
|
if (argv_ != nullptr)
|
||||||
|
{
|
||||||
|
wordexp_t we{
|
||||||
|
.we_wordc = static_cast<std::size_t>(argc_),
|
||||||
|
.we_wordv = argv_,
|
||||||
|
.we_offs = static_cast<std::size_t>(reserved_)
|
||||||
|
};
|
||||||
|
wordfree(&we);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto shell::args() const -> args_type
|
||||||
|
{
|
||||||
|
if (argc() == 0)
|
||||||
|
{
|
||||||
|
static const char * helper = nullptr;
|
||||||
|
return &helper;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return const_cast<const char**>(argv());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BOOST_PROCESS_V2_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif //BOOST_PROCESS_V2_IMPL_SHELL_IPP
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#define BOOST_PROCESS_V2_PID_HPP
|
#define BOOST_PROCESS_V2_PID_HPP
|
||||||
|
|
||||||
#include <boost/process/v2/detail/config.hpp>
|
#include <boost/process/v2/detail/config.hpp>
|
||||||
|
#include <boost/process/v2/detail/throw_error.hpp>
|
||||||
|
|
||||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@@ -31,6 +32,12 @@ typedef int pid_type;
|
|||||||
/// Get the process id of the current process.
|
/// Get the process id of the current process.
|
||||||
BOOST_PROCESS_V2_DECL pid_type current_pid();
|
BOOST_PROCESS_V2_DECL pid_type current_pid();
|
||||||
|
|
||||||
|
/// List all available pids.
|
||||||
|
BOOST_PROCESS_V2_DECL std::vector<pid_type> all_pids(error_code & ec);
|
||||||
|
|
||||||
|
/// List all available pids.
|
||||||
|
BOOST_PROCESS_V2_DECL std::vector<pid_type> all_pids();
|
||||||
|
|
||||||
BOOST_PROCESS_V2_END_NAMESPACE
|
BOOST_PROCESS_V2_END_NAMESPACE
|
||||||
|
|
||||||
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||||
|
|||||||
@@ -91,13 +91,16 @@ struct bind_fd
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||||
|
{
|
||||||
|
launcher.fd_whitelist.push_back(target);
|
||||||
|
}
|
||||||
|
|
||||||
/// Implementation of the initialization function.
|
/// Implementation of the initialization function.
|
||||||
error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||||
{
|
{
|
||||||
if (::dup2(fd, target) == -1)
|
if (::dup2(fd, target) == -1)
|
||||||
return error_code(errno, system_category());
|
return error_code(errno, system_category());
|
||||||
|
|
||||||
launcher.fd_whitelist.push_back(target);
|
|
||||||
return error_code ();
|
return error_code ();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ struct default_launcher
|
|||||||
detail::on_error(*this, executable, argv, ec, inits...);
|
detail::on_error(*this, executable, argv, ec, inits...);
|
||||||
return basic_process<Executor>(exec);
|
return basic_process<Executor>(exec);
|
||||||
}
|
}
|
||||||
|
fd_whitelist.push_back(pg.p[1]);
|
||||||
|
|
||||||
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
|
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
|
||||||
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
|
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
|
||||||
@@ -399,7 +400,6 @@ struct default_launcher
|
|||||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||||
if (!ec)
|
if (!ec)
|
||||||
{
|
{
|
||||||
fd_whitelist.push_back(pg.p[1]);
|
|
||||||
close_all_fds(ec);
|
close_all_fds(ec);
|
||||||
}
|
}
|
||||||
if (!ec)
|
if (!ec)
|
||||||
@@ -485,6 +485,11 @@ struct default_launcher
|
|||||||
return argv_.data();
|
return argv_.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char * const * build_argv_(const filesystem::path &, const char ** argv)
|
||||||
|
{
|
||||||
|
return argv;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Args>
|
template<typename Args>
|
||||||
const char * const * build_argv_(const filesystem::path & pt, const Args & args,
|
const char * const * build_argv_(const filesystem::path & pt, const Args & args,
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ struct pdfork_launcher : default_launcher
|
|||||||
detail::on_error(*this, executable, argv, ec, inits...);
|
detail::on_error(*this, executable, argv, ec, inits...);
|
||||||
return basic_process<Executor>(exec);
|
return basic_process<Executor>(exec);
|
||||||
}
|
}
|
||||||
|
fd_whitelist.push_back(pg.p[1]);
|
||||||
|
|
||||||
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
|
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
|
||||||
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
|
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
|
||||||
@@ -121,7 +122,6 @@ struct pdfork_launcher : default_launcher
|
|||||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||||
if (!ec)
|
if (!ec)
|
||||||
{
|
{
|
||||||
fd_whitelist.push_back(pg.p[1]);
|
|
||||||
close_all_fds(ec);
|
close_all_fds(ec);
|
||||||
}
|
}
|
||||||
if (!ec)
|
if (!ec)
|
||||||
|
|||||||
@@ -19,9 +19,11 @@
|
|||||||
|
|
||||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||||
#include <asio/any_io_executor.hpp>
|
#include <asio/any_io_executor.hpp>
|
||||||
|
#include <asio/post.hpp>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#else
|
#else
|
||||||
#include <boost/asio/any_io_executor.hpp>
|
#include <boost/asio/any_io_executor.hpp>
|
||||||
|
#include <boost/asio/post.hpp>
|
||||||
#include <boost/core/exchange.hpp>
|
#include <boost/core/exchange.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -164,7 +166,7 @@ struct basic_process
|
|||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_convertible<ExecutionContext&,
|
std::is_convertible<ExecutionContext&,
|
||||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
|
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
|
||||||
: process_handle_(context, pid, native_handle) {}
|
: process_handle_(context.get_executor(), pid, native_handle) {}
|
||||||
|
|
||||||
/// Create an invalid handle
|
/// Create an invalid handle
|
||||||
template <typename ExecutionContext>
|
template <typename ExecutionContext>
|
||||||
@@ -172,7 +174,7 @@ struct basic_process
|
|||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
is_convertible<ExecutionContext&,
|
is_convertible<ExecutionContext&,
|
||||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
|
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, void *>::type = nullptr)
|
||||||
: process_handle_(context) {}
|
: process_handle_(context.get_executor()) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -212,6 +214,37 @@ struct basic_process
|
|||||||
process_handle_.request_exit(ec);
|
process_handle_.request_exit(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send the process a signal requesting it to stop. This may rely on undocmented functions.
|
||||||
|
void suspend(error_code &ec)
|
||||||
|
{
|
||||||
|
process_handle_.suspend(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send the process a signal requesting it to stop. This may rely on undocmented functions.
|
||||||
|
void suspend()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
suspend(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "suspend");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Send the process a signal requesting it to resume. This may rely on undocmented functions.
|
||||||
|
void resume(error_code &ec)
|
||||||
|
{
|
||||||
|
process_handle_.resume(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send the process a signal requesting it to resume. This may rely on undocmented functions.
|
||||||
|
void resume()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
suspend(ec);
|
||||||
|
if (ec)
|
||||||
|
detail::throw_error(ec, "resume");
|
||||||
|
}
|
||||||
|
|
||||||
/// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
|
/// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
|
||||||
void terminate()
|
void terminate()
|
||||||
{
|
{
|
||||||
@@ -339,7 +372,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(),
|
BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(),
|
||||||
completer{res, std::move(self)});
|
completer{static_cast<int>(res), std::move(self)});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
handle.async_wait(std::move(self));
|
handle.async_wait(std::move(self));
|
||||||
|
|||||||
139
include/boost/process/v2/shell.hpp
Normal file
139
include/boost/process/v2/shell.hpp
Normal file
@@ -0,0 +1,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
|
||||||
@@ -22,5 +22,6 @@
|
|||||||
#include <boost/process/v2/impl/default_launcher.ipp>
|
#include <boost/process/v2/impl/default_launcher.ipp>
|
||||||
#include <boost/process/v2/impl/environment.ipp>
|
#include <boost/process/v2/impl/environment.ipp>
|
||||||
#include <boost/process/v2/impl/process_handle.ipp>
|
#include <boost/process/v2/impl/process_handle.ipp>
|
||||||
|
#include <boost/process/v2/impl/shell.ipp>
|
||||||
|
|
||||||
#endif //BOOST_PROCESS_V2_SRC_HPP
|
#endif //BOOST_PROCESS_V2_SRC_HPP
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
#include <boost/process/v2/detail/config.hpp>
|
#include <boost/process/v2/detail/config.hpp>
|
||||||
#include <boost/process/v2/default_launcher.hpp>
|
#include <boost/process/v2/default_launcher.hpp>
|
||||||
|
#include <cstddef>
|
||||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||||
#include <asio/connect_pipe.hpp>
|
#include <asio/connect_pipe.hpp>
|
||||||
#else
|
#else
|
||||||
@@ -52,7 +52,7 @@ struct handle_closer
|
|||||||
DWORD flags{0xFFFFFFFFu};
|
DWORD flags{0xFFFFFFFFu};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<DWORD Io>
|
template<DWORD Target>
|
||||||
struct process_io_binding
|
struct process_io_binding
|
||||||
{
|
{
|
||||||
HANDLE prepare()
|
HANDLE prepare()
|
||||||
@@ -62,7 +62,7 @@ struct process_io_binding
|
|||||||
return hh;
|
return hh;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<void, handle_closer> h{::GetStdHandle(Io), false};
|
std::unique_ptr<void, handle_closer> h{::GetStdHandle(Target), false};
|
||||||
|
|
||||||
static DWORD get_flags(HANDLE h)
|
static DWORD get_flags(HANDLE h)
|
||||||
{
|
{
|
||||||
@@ -82,10 +82,11 @@ struct process_io_binding
|
|||||||
process_io_binding(FILE * f) : process_io_binding(_get_osfhandle(_fileno(f))) {}
|
process_io_binding(FILE * f) : process_io_binding(_get_osfhandle(_fileno(f))) {}
|
||||||
process_io_binding(HANDLE h) : h{h, get_flags(h)} {}
|
process_io_binding(HANDLE h) : h{h, get_flags(h)} {}
|
||||||
process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("NUL")) {}
|
process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("NUL")) {}
|
||||||
process_io_binding(const filesystem::path & pth)
|
template<typename T, typename = typename std::enable_if<std::is_same<T, filesystem::path>::value>::type>
|
||||||
|
process_io_binding(const T & pth)
|
||||||
: h(::CreateFileW(
|
: h(::CreateFileW(
|
||||||
pth.c_str(),
|
pth.c_str(),
|
||||||
Io == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE,
|
Target == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE,
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
nullptr,
|
nullptr,
|
||||||
OPEN_ALWAYS,
|
OPEN_ALWAYS,
|
||||||
@@ -101,11 +102,13 @@ struct process_io_binding
|
|||||||
typename std::enable_if<Target != STD_INPUT_HANDLE, Executor*>::type = 0)
|
typename std::enable_if<Target != STD_INPUT_HANDLE, Executor*>::type = 0)
|
||||||
{
|
{
|
||||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
||||||
|
error_code ec;
|
||||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
||||||
if (ec)
|
if (ec)
|
||||||
return ;
|
detail::throw_error(ec, "create_pipe");
|
||||||
|
|
||||||
h = std::unique_ptr<void, handle_closer>{p[1], true};
|
h = std::unique_ptr<void, handle_closer>{p[1], true};
|
||||||
readable_pipe.assign(p[0], ec);
|
readable_pipe.assign(p[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -114,11 +117,13 @@ struct process_io_binding
|
|||||||
typename std::enable_if<Target == STD_INPUT_HANDLE, Executor*>::type = 0)
|
typename std::enable_if<Target == STD_INPUT_HANDLE, Executor*>::type = 0)
|
||||||
{
|
{
|
||||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
||||||
|
error_code ec;
|
||||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
||||||
if (ec)
|
if (ec)
|
||||||
return ;
|
detail::throw_error(ec, "create_pipe");
|
||||||
|
|
||||||
h = std::unique_ptr<void, handle_closer>{p[0], true};
|
h = std::unique_ptr<void, handle_closer>{p[0], true};
|
||||||
writable_pipe.assign(p[1], ec);
|
writable_pipe.assign(p[1]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -289,11 +294,6 @@ struct process_stdio
|
|||||||
if (::dup2(err.fd, err.target) == -1)
|
if (::dup2(err.fd, err.target) == -1)
|
||||||
return error_code(errno, system_category());
|
return error_code(errno, system_category());
|
||||||
|
|
||||||
|
|
||||||
launcher.fd_whitelist.push_back(STDIN_FILENO);
|
|
||||||
launcher.fd_whitelist.push_back(STDOUT_FILENO);
|
|
||||||
launcher.fd_whitelist.push_back(STDERR_FILENO);
|
|
||||||
|
|
||||||
return error_code {};
|
return error_code {};
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -398,6 +398,11 @@ struct default_launcher
|
|||||||
return build_command_line_impl(pt, args, *std::begin(args));
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -113,3 +113,17 @@ BOOST_AUTO_TEST_CASE(ignore_error)
|
|||||||
BOOST_CHECK_NO_THROW(bp::child c("doesnt-exit", bp::ignore_error));
|
BOOST_CHECK_NO_THROW(bp::child c("doesnt-exit", bp::ignore_error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(not_found)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bp::child c("doesnt-exit");
|
||||||
|
BOOST_CHECK_MESSAGE(false, "Should throw");
|
||||||
|
}
|
||||||
|
catch( bp::process_error & se)
|
||||||
|
{
|
||||||
|
BOOST_CHECK(se.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ boost_process_v2_standalone_test(utf8)
|
|||||||
boost_process_v2_standalone_test(cstring_ref)
|
boost_process_v2_standalone_test(cstring_ref)
|
||||||
boost_process_v2_standalone_test(pid)
|
boost_process_v2_standalone_test(pid)
|
||||||
boost_process_v2_standalone_test(environment)
|
boost_process_v2_standalone_test(environment)
|
||||||
|
boost_process_v2_standalone_test(shell)
|
||||||
|
|
||||||
add_library(boost_process_v2_header_test header_1.cpp header_2.cpp)
|
add_library(boost_process_v2_header_test header_1.cpp header_2.cpp)
|
||||||
target_link_libraries(boost_process_v2_header_test PUBLIC Boost::process)
|
target_link_libraries(boost_process_v2_header_test PUBLIC Boost::process)
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ lib test_impl : test_impl.cpp filesystem :
|
|||||||
<link>static
|
<link>static
|
||||||
<target-os>windows:<source>shell32
|
<target-os>windows:<source>shell32
|
||||||
<target-os>windows:<source>user32
|
<target-os>windows:<source>user32
|
||||||
|
<target-os>windows:<source>Ntdll
|
||||||
;
|
;
|
||||||
|
|
||||||
test-suite standalone :
|
test-suite standalone :
|
||||||
@@ -53,6 +54,7 @@ test-suite standalone :
|
|||||||
[ run cstring_ref.cpp test_impl ]
|
[ run cstring_ref.cpp test_impl ]
|
||||||
[ run environment.cpp test_impl ]
|
[ run environment.cpp test_impl ]
|
||||||
[ run pid.cpp test_impl ]
|
[ run pid.cpp test_impl ]
|
||||||
|
[ run shell.cpp test_impl ]
|
||||||
;
|
;
|
||||||
|
|
||||||
test-suite with_target :
|
test-suite with_target :
|
||||||
|
|||||||
@@ -95,9 +95,9 @@ BOOST_AUTO_TEST_CASE(environment)
|
|||||||
#else
|
#else
|
||||||
std::unordered_map<std::wstring, std::wstring> custom_env =
|
std::unordered_map<std::wstring, std::wstring> custom_env =
|
||||||
{
|
{
|
||||||
L"HOME", L"/home/byzantium",
|
{L"HOME", L"/home/byzantium"},
|
||||||
L"HOMEDRIVE", L"X:",
|
{L"HOMEDRIVE", L"X:"},
|
||||||
L"HOMEPATH", L"\\users\\theodora"
|
{L"HOMEPATH", L"\\users\\theodora"}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::wstring> custom_env2 =
|
std::vector<std::wstring> custom_env2 =
|
||||||
@@ -106,8 +106,8 @@ BOOST_AUTO_TEST_CASE(environment)
|
|||||||
{L"HOMEDRIVE=X:"},
|
{L"HOMEDRIVE=X:"},
|
||||||
{L"HOMEPATH=\\users\\theodora"}
|
{L"HOMEPATH=\\users\\theodora"}
|
||||||
};
|
};
|
||||||
BOOST_CHECK_EQUAL(bpe::home(custom_env), L"X:\\Users\\theodora");
|
BOOST_CHECK_EQUAL(bpe::home(custom_env), "X:\\users\\theodora");
|
||||||
BOOST_CHECK_EQUAL(bpe::home(custom_env2), L"X:\\Users\\theodora");
|
BOOST_CHECK_EQUAL(bpe::home(custom_env2), "X:\\users\\theodora");
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -12,4 +12,8 @@ BOOST_AUTO_TEST_CASE(test_pid)
|
|||||||
{
|
{
|
||||||
namespace bp2 = boost::process::v2;
|
namespace bp2 = boost::process::v2;
|
||||||
BOOST_CHECK_NE(bp2::current_pid(), static_cast<bp2::pid_type>(0));
|
BOOST_CHECK_NE(bp2::current_pid(), static_cast<bp2::pid_type>(0));
|
||||||
|
|
||||||
|
auto all = bp2::all_pids();
|
||||||
|
auto itr = std::find(all.begin(), all.end(), bp2::current_pid());
|
||||||
|
BOOST_CHECK(itr != all.end());
|
||||||
}
|
}
|
||||||
@@ -149,6 +149,8 @@ BOOST_AUTO_TEST_CASE(terminate)
|
|||||||
|
|
||||||
BOOST_CHECK_MESSAGE(!sh.empty(), sh);
|
BOOST_CHECK_MESSAGE(!sh.empty(), sh);
|
||||||
bpv::process proc(ctx, sh, {});
|
bpv::process proc(ctx, sh, {});
|
||||||
|
proc.suspend();
|
||||||
|
proc.resume();
|
||||||
proc.terminate();
|
proc.terminate();
|
||||||
proc.wait();
|
proc.wait();
|
||||||
}
|
}
|
||||||
@@ -164,6 +166,7 @@ BOOST_AUTO_TEST_CASE(request_exit)
|
|||||||
, asio::windows::show_window_minimized_not_active
|
, asio::windows::show_window_minimized_not_active
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
BOOST_CHECK(proc.running());
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||||
proc.request_exit();
|
proc.request_exit();
|
||||||
proc.wait();
|
proc.wait();
|
||||||
@@ -188,6 +191,8 @@ void trim_end(std::string & str)
|
|||||||
{
|
{
|
||||||
auto itr = std::find_if(str.rbegin(), str.rend(), &std::char_traits<char>::not_eof);
|
auto itr = std::find_if(str.rbegin(), str.rend(), &std::char_traits<char>::not_eof);
|
||||||
str.erase(itr.base(), str.end());
|
str.erase(itr.base(), str.end());
|
||||||
|
if (!str.empty() && str.back() == '\r')
|
||||||
|
str.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(print_args_out)
|
BOOST_AUTO_TEST_CASE(print_args_out)
|
||||||
@@ -356,16 +361,16 @@ BOOST_AUTO_TEST_CASE(popen)
|
|||||||
// default CWD
|
// default CWD
|
||||||
bpv::popen proc(ctx, pth, {"echo"});
|
bpv::popen proc(ctx, pth, {"echo"});
|
||||||
|
|
||||||
asio::write(proc, asio::buffer("FOOBAR"));
|
auto written = asio::write(proc, asio::buffer("FOOBAR"));
|
||||||
|
|
||||||
proc.get_stdin().close();
|
proc.get_stdin().close();
|
||||||
|
|
||||||
std::string res;
|
std::string res;
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
std::size_t n = asio::read(proc, asio::dynamic_buffer(res), ec);
|
std::size_t n = asio::read(proc, asio::dynamic_buffer(res), ec);
|
||||||
res.resize(n - 1);
|
BOOST_CHECK(ec == asio::error::eof || ec == asio::error::broken_pipe);
|
||||||
BOOST_CHECK_EQUAL(ec, asio::error::eof);
|
BOOST_REQUIRE_GE(n, 1);
|
||||||
// remove EOF
|
// remove EOF
|
||||||
|
res.pop_back();
|
||||||
BOOST_CHECK_EQUAL(res, "FOOBAR");
|
BOOST_CHECK_EQUAL(res, "FOOBAR");
|
||||||
|
|
||||||
proc.wait();
|
proc.wait();
|
||||||
@@ -429,7 +434,7 @@ std::string read_env(const char * name, Inits && ... inits)
|
|||||||
BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message());
|
BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message());
|
||||||
out.resize(sz);
|
out.resize(sz);
|
||||||
trim_end(out);
|
trim_end(out);
|
||||||
printf("Read env (%ld) %s: '%s'\n", sz, name, out.c_str());
|
printf("Read env (%ld) %s: '%s'\n", static_cast<long>(sz), name, out.c_str());
|
||||||
|
|
||||||
proc.wait();
|
proc.wait();
|
||||||
BOOST_CHECK_EQUAL(proc.exit_code(), 0);
|
BOOST_CHECK_EQUAL(proc.exit_code(), 0);
|
||||||
@@ -449,12 +454,12 @@ BOOST_AUTO_TEST_CASE(environment)
|
|||||||
BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{sub_env}));
|
BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{sub_env}));
|
||||||
|
|
||||||
sub_env.push_back("XYZ=ZYX");
|
sub_env.push_back("XYZ=ZYX");
|
||||||
auto itr = std::find_if(sub_env.begin(), sub_env.end(), [](const bpv::environment::key_value_pair & kv) {return kv.key() == "PATH";});
|
auto itr = std::find_if(sub_env.begin(), sub_env.end(), [](const bpv::environment::key_value_pair & kv) {return kv.key() == bpv::environment::key("PATH");});
|
||||||
path += static_cast<char>(bpv::environment::delimiter);
|
path += static_cast<char>(bpv::environment::delimiter);
|
||||||
path += "/bar/foo";
|
path += "/bar/foo";
|
||||||
bpv::environment::value pval = itr->value();
|
bpv::environment::value pval = itr->value();
|
||||||
pval.push_back("/bar/foo");
|
pval.push_back("/bar/foo");
|
||||||
*itr = bpv::environment::key_value_pair("PATH", pval);
|
*itr = bpv::environment::key_value_pair(bpv::environment::key("PATH"), pval);
|
||||||
BOOST_CHECK_EQUAL(path, read_env("PATH", bpv::process_environment{sub_env}));
|
BOOST_CHECK_EQUAL(path, read_env("PATH", bpv::process_environment{sub_env}));
|
||||||
|
|
||||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
@@ -462,12 +467,13 @@ BOOST_AUTO_TEST_CASE(environment)
|
|||||||
BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{L"FOOBAR=FOO-BAR", wpath.c_str()}));
|
BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{L"FOOBAR=FOO-BAR", wpath.c_str()}));
|
||||||
wpath += bpv::environment::delimiter;
|
wpath += bpv::environment::delimiter;
|
||||||
wpath += L"C:\\bar\\foo";
|
wpath += L"C:\\bar\\foo";
|
||||||
BOOST_CHECK_EQUAL(wpath.substr(5), read_env("pATH", bpv::process_environment{wpath.c_str(), std::wstring(L"XYZ=ZYX")}));
|
BOOST_CHECK_EQUAL(
|
||||||
|
bpv::detail::conv_string<char>(wpath.c_str() + 5, wpath.size() - 5)
|
||||||
|
, read_env("pATH", bpv::process_environment{wpath.c_str(), std::wstring(L"XYZ=ZYX")}));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(read_env("PATH", bpv::process_environment(bpv::environment::current())), ::getenv("PATH"));
|
BOOST_CHECK_EQUAL(read_env("PATH", bpv::process_environment(bpv::environment::current())), ::getenv("PATH"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END();
|
BOOST_AUTO_TEST_SUITE_END();
|
||||||
|
|
||||||
|
|||||||
56
test/v2/shell.cpp
Executable file
56
test/v2/shell.cpp
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
// 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)
|
||||||
|
|
||||||
|
// Disable autolinking for unit tests.
|
||||||
|
#if !defined(BOOST_ALL_NO_LIB)
|
||||||
|
#define BOOST_ALL_NO_LIB 1
|
||||||
|
#endif // !defined(BOOST_ALL_NO_LIB)
|
||||||
|
|
||||||
|
// Test that header file is self-contained.
|
||||||
|
#include <boost/process/v2/shell.hpp>
|
||||||
|
#include <boost/process/v2/process.hpp>
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
|
#define STR(Value) L##Value
|
||||||
|
#define STR_VIEW(Value) boost::process::v2::wcstring_ref(STR(Value))
|
||||||
|
#else
|
||||||
|
#define STR(Value) Value
|
||||||
|
#define STR_VIEW(Value) boost::process::v2::cstring_ref(STR(Value))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_shell_parser)
|
||||||
|
{
|
||||||
|
using boost::process::v2::shell;
|
||||||
|
namespace bpv = boost::process::v2;
|
||||||
|
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||||
|
BOOST_CHECK_THROW(shell("foo \""), bpv::system_error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto sh = shell(STR("foo bar \"foo bar\""));
|
||||||
|
BOOST_CHECK(sh.argc() == 3u);
|
||||||
|
BOOST_CHECK(sh.argv()[0] == STR_VIEW("foo"));
|
||||||
|
BOOST_CHECK(sh.argv()[1] == STR_VIEW("bar"));
|
||||||
|
BOOST_CHECK(sh.argv()[2] == STR_VIEW("foo bar"));
|
||||||
|
|
||||||
|
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||||
|
auto raw_shell = "sh -c false";
|
||||||
|
#else
|
||||||
|
auto raw_shell = "cmd /c exit 1";
|
||||||
|
#endif
|
||||||
|
sh = shell(raw_shell);
|
||||||
|
|
||||||
|
auto exe = sh.exe();
|
||||||
|
BOOST_CHECK(bpv::filesystem::exists(exe));
|
||||||
|
|
||||||
|
boost::asio::io_context ctx;
|
||||||
|
bpv::process proc{ctx, exe, sh.args()};
|
||||||
|
|
||||||
|
proc.wait();
|
||||||
|
BOOST_CHECK_EQUAL(proc.exit_code(), 1);
|
||||||
|
}
|
||||||
@@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(creation_flags)
|
|||||||
BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, 0);
|
BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, 0);
|
||||||
|
|
||||||
proc = bpv::process{ctx, master_test_suite().argv[1], {"creation-flags"}, bpv::windows::process_creation_flags<STARTF_TITLEISAPPID>()};
|
proc = bpv::process{ctx, master_test_suite().argv[1], {"creation-flags"}, bpv::windows::process_creation_flags<STARTF_TITLEISAPPID>()};
|
||||||
BOOST_CHECK(proc);
|
BOOST_CHECK(proc.running());
|
||||||
BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, STARTF_TITLEISAPPID);
|
BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, STARTF_TITLEISAPPID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user