2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-22 17:32:42 +00:00

Compare commits

...

37 Commits

Author SHA1 Message Date
Klemens
9edacbdb98 ec fix for search_path with std::filesystem.
closes #287.
2023-01-04 08:11:22 +08:00
Klemens Morgenstern
a9f083f45c Update process.cpp 2022-12-13 13:50:03 +08:00
Klemens Morgenstern
b5ab94c932 Disabled some tests for freebsd & added interrupt handling to osx test. 2022-12-13 10:45:47 +08:00
sdarwin
9ceda79fa2 Update metadata 2022-12-13 09:22:13 +08:00
Klemens Morgenstern
68cbeae491 Typo fix. 2022-12-13 09:19:45 +08:00
Klemens Morgenstern
ae778f36a8 Include fixes. 2022-11-11 11:14:46 +08:00
Sam Darwin
0671e4a133 Drone: update freebsd jobs (#274) 2022-11-01 10:40:04 +08:00
Klemens Morgenstern
cbf944c57b using scope-exit limit group_wait. 2022-11-01 03:08:12 +08:00
Klemens Morgenstern
5f2b8c53f4 Disabled limit_fd for freebsd. 2022-11-01 02:58:31 +08:00
Klemens Morgenstern
dac3614a38 group wait test on_scope exit fix. 2022-10-31 12:25:30 +08:00
Klemens Morgenstern
2fc71ca0a3 Disabled pdfork by default, bc of asio errors. 2022-10-31 11:23:49 +08:00
Klemens Morgenstern
404682d75d Increased timeout for sporadically failing test. 2022-10-31 11:21:48 +08:00
Klemens Morgenstern
9fbbdc3421 Merge pull request #276 from Flamefire/patch-1
Update .drone.star
2022-10-23 11:21:09 +08:00
Alexander Grund
3df0009a2f Update .drone.star
Remove the `image` param which is superflous, misleading and may become an error. See https://github.com/boostorg/boost-ci/pull/189

[skip ci]
2022-10-22 11:26:04 +02:00
Klemens Morgenstern
ecb384b253 Improved error message for OSX. 2022-10-21 12:03:31 +08:00
Klemens Morgenstern
05bce942c1 passing a pipe into sh test. 2022-10-12 11:54:05 +08:00
Klemens Morgenstern
dcf5d8ce41 Added return_type to async_result<code_as_error_t> 2022-10-12 10:54:30 +08:00
Klemens Morgenstern
4209f8ee6e Minor bugfixes 2022-10-12 01:12:28 +08:00
Klemens Morgenstern
d9513269cc Enabled freebsd build 2022-10-11 21:03:33 +08:00
Klemens
293f28dab6 Fixed async_system. 2022-09-20 13:45:43 +08:00
Klemens
fe1b629b5d Added bind_launcher. 2022-09-18 22:13:57 +08:00
Klemens
278fa57214 Added code_as_error completion handler. 2022-09-18 17:56:47 +08:00
Klemens Morgenstern
1addfba12e Added WIN32_LEAN_AND_MEAN to cmake 2022-09-17 20:39:01 +08:00
Klemens Morgenstern
4cc469b2a4 Merge pull request #252 from grtowel1510f/patch-1
fix issue #251 - fix simple shell command in posix
2022-09-14 11:38:37 +08:00
Klemens Morgenstern
6e4d1e29d2 Merge pull request #271 from boostorg/shell_v2
Shell v2
2022-09-02 20:30:03 +08:00
Klemens Morgenstern
dada865fd0 Merge pull request #269 from boostorg/klemens-morgenstern-patch-2
Closes #266
2022-09-02 20:29:20 +08:00
Klemens Morgenstern
380dd1b00f Merge pull request #268 from boostorg/klemens-morgenstern-patch-1
Closes #267
2022-09-02 20:28:50 +08:00
Klemens
7832cb6af3 Shell(posix) fixes. 2022-09-02 18:43:35 +08:00
Klemens Morgenstern
68f4c50be9 Exeuction support for shell. 2022-09-02 18:25:40 +08:00
Klemens Morgenstern
cd226a7616 Implemented shell on windows. 2022-09-02 17:05:49 +08:00
Klemens Morgenstern
4243ce72f8 Windows bugfixes. 2022-09-02 16:46:45 +08:00
Klemens
9065833e61 Added shell class. 2022-08-31 23:54:22 +08:00
Klemens Morgenstern
7cb7af6c8b Closes #267 2022-08-31 15:40:57 +08:00
Klemens Morgenstern
90cbe7cec0 Closes #266 2022-08-31 15:35:51 +08:00
Klemens
df33c1ad7b Fixed unsafe post-fork allocs for fd_whitelist. 2022-08-31 15:35:41 +08:00
Klemens
bbabea30dd Added reaping child for execve error, closes #265. 2022-08-31 15:35:41 +08:00
grtowel1510f
8a61f8daa3 fix issue #251 - fix simple shell command in posix
see issue #251 for description.
2022-05-21 14:59:37 +00:00
37 changed files with 1099 additions and 157 deletions

View File

@@ -14,7 +14,8 @@ windowsglobalimage="cppalliance/dronevs2019"
def main(ctx): def main(ctx):
return [ return [
#freebsd_cxx("FreeBSD", "g++10", packages="g++10", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), freebsd_cxx("gcc 11 freebsd", "g++-11", buildtype="boost", buildscript="drone", freebsd_version="13.1", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,20', 'B2_LINKFLAGS': '-Wl,-rpath=/usr/local/lib/gcc11'}, globalenv=globalenv),
freebsd_cxx("clang 14 freebsd", "clang++-14", buildtype="boost", buildscript="drone", freebsd_version="13.1", environment={'B2_TOOLSET': 'clang-14', 'B2_CXXSTD': '17,20'}, globalenv=globalenv),
linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv), linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv),
linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True),
linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv),

View File

@@ -1,4 +1,4 @@
[section:quickstart Quickstrat] [section:quickstart Quickstart]
A process needs four things to be launched: A process needs four things to be launched:

View File

@@ -38,12 +38,11 @@ namespace process {
namespace detail namespace detail
{ {
template<typename ExitHandler> template<typename Handler>
struct async_system_handler : ::boost::process::detail::api::async_handler struct async_system_handler : ::boost::process::detail::api::async_handler
{ {
boost::asio::io_context & ios; boost::asio::io_context & ios;
boost::asio::async_completion< Handler handler;
ExitHandler, void(boost::system::error_code, int)> init;
#if defined(BOOST_POSIX_API) #if defined(BOOST_POSIX_API)
bool errored = false; bool errored = false;
@@ -52,9 +51,8 @@ struct async_system_handler : ::boost::process::detail::api::async_handler
template<typename ExitHandler_> template<typename ExitHandler_>
async_system_handler( async_system_handler(
boost::asio::io_context & ios, boost::asio::io_context & ios,
ExitHandler_ && exit_handler) : ios(ios), init(exit_handler) ExitHandler_ && exit_handler) : ios(ios), handler(std::forward<ExitHandler_>(exit_handler))
{ {
} }
@@ -64,21 +62,15 @@ struct async_system_handler : ::boost::process::detail::api::async_handler
#if defined(BOOST_POSIX_API) #if defined(BOOST_POSIX_API)
errored = true; errored = true;
#endif #endif
auto & h = init.completion_handler; auto h = std::make_shared<Handler>(std::move(handler));
boost::asio::post( boost::asio::post(
ios.get_executor(), ios.get_executor(),
[h, ec]() mutable [h, ec]() mutable
{ {
h(boost::system::error_code(ec.value(), boost::system::system_category()), -1); (*h)(boost::system::error_code(ec.value(), boost::system::system_category()), -1);
}); });
} }
BOOST_ASIO_INITFN_RESULT_TYPE(ExitHandler, void (boost::system::error_code, int))
get_result()
{
return init.result.get();
}
template<typename Executor> template<typename Executor>
std::function<void(int, const std::error_code&)> on_exit_handler(Executor&) std::function<void(int, const std::error_code&)> on_exit_handler(Executor&)
{ {
@@ -86,10 +78,10 @@ struct async_system_handler : ::boost::process::detail::api::async_handler
if (errored) if (errored)
return [](int , const std::error_code &){}; return [](int , const std::error_code &){};
#endif #endif
auto & h = init.completion_handler; auto h = std::make_shared<Handler>(std::move(handler));
return [h](int exit_code, const std::error_code & ec) mutable return [h](int exit_code, const std::error_code & ec) mutable
{ {
h(boost::system::error_code(ec.value(), boost::system::system_category()), exit_code); (*h)(boost::system::error_code(ec.value(), boost::system::system_category()), exit_code);
}; };
} }
}; };
@@ -120,21 +112,36 @@ inline boost::process::detail::dummy
async_system(boost::asio::io_context & ios, ExitHandler && exit_handler, Args && ...args); async_system(boost::asio::io_context & ios, ExitHandler && exit_handler, Args && ...args);
#endif #endif
namespace detail
{
struct async_system_init_op
{
template<typename Handler, typename ... Args>
void operator()(Handler && handler, asio::io_context & ios, Args && ... args)
{
detail::async_system_handler<typename std::decay<Handler>::type> async_h{ios, std::forward<Handler>(handler)};
child(ios, std::forward<Args>(args)..., async_h ).detach();
}
};
}
template<typename ExitHandler, typename ...Args> template<typename ExitHandler, typename ...Args>
inline BOOST_ASIO_INITFN_RESULT_TYPE(ExitHandler, void (boost::system::error_code, int)) inline BOOST_ASIO_INITFN_RESULT_TYPE(ExitHandler, void (boost::system::error_code, int))
async_system(boost::asio::io_context & ios, ExitHandler && exit_handler, Args && ...args) async_system(boost::asio::io_context & ios, ExitHandler && exit_handler, Args && ...args)
{ {
detail::async_system_handler<ExitHandler> async_h{ios, std::forward<ExitHandler>(exit_handler)};
typedef typename ::boost::process::detail::has_error_handler<boost::fusion::tuple<Args...>>::type typedef typename ::boost::process::detail::has_error_handler<boost::fusion::tuple<Args...>>::type
has_err_handling; has_err_handling;
static_assert(!has_err_handling::value, "async_system cannot have custom error handling"); static_assert(!has_err_handling::value, "async_system cannot have custom error handling");
return boost::asio::async_initiate<ExitHandler, void (boost::system::error_code, int)>(
child(ios, std::forward<Args>(args)..., async_h ).detach(); detail::async_system_init_op{}, exit_handler, ios, std::forward<Args>(args)...
);
return async_h.get_result();
} }

View File

@@ -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(

View File

@@ -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();
} }

View File

@@ -27,7 +27,11 @@ inline boost::process::filesystem::path search_path(
for (const boost::process::filesystem::path & pp : path) for (const boost::process::filesystem::path & pp : path)
{ {
auto p = pp / filename; auto p = pp / filename;
#if defined(BOOST_PROCESS_USE_STD_FS)
std::error_code ec;
#else
boost::system::error_code ec; boost::system::error_code ec;
#endif
bool file = boost::process::filesystem::is_regular_file(p, ec); bool file = boost::process::filesystem::is_regular_file(p, ec);
if (!ec && file && ::access(p.c_str(), X_OK) == 0) if (!ec && file && ::access(p.c_str(), X_OK) == 0)
return p; return p;

View File

@@ -61,7 +61,11 @@ inline boost::process::filesystem::path search_path(
{ {
boost::process::filesystem::path pp_ext = p; boost::process::filesystem::path pp_ext = p;
pp_ext += ext; pp_ext += ext;
#if defined(BOOST_PROCESS_USE_STD_FS)
std::error_code ec;
#else
boost::system::error_code ec; boost::system::error_code ec;
#endif
bool file = boost::process::filesystem::is_regular_file(pp_ext, ec); bool file = boost::process::filesystem::is_regular_file(pp_ext, ec);
if (!ec && file && if (!ec && file &&
::boost::winapi::sh_get_file_info(pp_ext.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_)) ::boost::winapi::sh_get_file_info(pp_ext.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))

View File

@@ -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;

View File

@@ -0,0 +1,240 @@
//
// boost/process/v2/bind_launcher.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
//
// 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_BIND_LAUNCHER_HPP
#define BOOST_PROCESS_V2_BIND_LAUNCHER_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
template<std::size_t ... Idx>
struct index_sequence { };
template<std::size_t Size, typename T>
struct make_index_sequence_impl;
template<std::size_t Size, std::size_t ... Idx>
struct make_index_sequence_impl<Size, index_sequence<Idx...>>
{
constexpr make_index_sequence_impl() {}
using type = typename make_index_sequence_impl<Size - 1u, index_sequence<Size - 1u, Idx...>>::type;
};
template<std::size_t ... Idx>
struct make_index_sequence_impl<0u, index_sequence<Idx...>>
{
constexpr make_index_sequence_impl() {}
using type = index_sequence<Idx...>;
};
template<std::size_t Cnt>
struct make_index_sequence
{
using type = typename make_index_sequence_impl<Cnt, index_sequence<>>::type;
};
template<std::size_t Cnt>
using make_index_sequence_t = typename make_index_sequence<Cnt>::type;
}
/** @brief Utility class to bind initializers to a launcher
* @tparam Launcher The inner launcher to be used
* @tparam ...Init The initializers to be prepended.
*
* This can be used when multiple processes shared some settings,
* e.g.
*
*/
template<typename Launcher, typename ... Init>
struct bound_launcher
{
template<typename Launcher_, typename ... Init_>
bound_launcher(Launcher_ && l, Init_ && ... init) :
launcher_(std::forward<Launcher_>(l)), init_(std::forward<Init_>(init)...)
{
}
template<typename ExecutionContext, typename Args, typename ... Inits>
auto operator()(ExecutionContext & context,
const typename std::enable_if<std::is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits) -> basic_process<typename ExecutionContext::executor_type>
{
return invoke(detail::make_index_sequence_t<sizeof...(Init)>{},
context,
executable,
std::forward<Args>(args),
std::forward<Inits>(inits)...);
}
template<typename ExecutionContext, typename Args, typename ... Inits>
auto operator()(ExecutionContext & context,
error_code & ec,
const typename std::enable_if<std::is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
{
return invoke(detail::make_index_sequence_t<sizeof...(Init)>{},
context, ec,
executable,
std::forward<Args>(args),
std::forward<Inits>(inits)...);
}
template<typename Executor, typename Args, typename ... Inits>
auto operator()(Executor exec,
const typename std::enable_if<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<Executor>
{
return invoke(detail::make_index_sequence_t<sizeof...(Init)>{},
std::move(exec),
executable,
std::forward<Args>(args),
std::forward<Inits>(inits)...);
}
template<typename Executor, typename Args, typename ... Inits>
auto operator()(Executor exec,
error_code & ec,
const typename std::enable_if<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<Executor>
{
return invoke(detail::make_index_sequence_t<sizeof...(Init)>{},
std::move(exec), ec,
executable,
std::forward<Args>(args),
std::forward<Inits>(inits)...);
}
private:
template<std::size_t ... Idx, typename ExecutionContext, typename Args, typename ... Inits>
auto invoke(detail::index_sequence<Idx...>,
ExecutionContext & context,
const typename std::enable_if<std::is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits) -> basic_process<typename ExecutionContext::executor_type>
{
return launcher_(context,
executable,
std::forward<Args>(args),
std::get<Idx>(init_)...,
std::forward<Inits>(inits)...);
}
template<std::size_t ... Idx, typename ExecutionContext, typename Args, typename ... Inits>
auto invoke(detail::index_sequence<Idx...>,
ExecutionContext & context,
error_code & ec,
const typename std::enable_if<std::is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
{
return launcher_(context, ec,
executable,
std::forward<Args>(args),
std::get<Idx>(init_)...,
std::forward<Inits>(inits)...);
}
template<std::size_t ... Idx, typename Executor, typename Args, typename ... Inits>
auto invoke(detail::index_sequence<Idx...>,
Executor exec,
const typename std::enable_if<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<Executor>
{
return launcher_(std::move(exec),
executable,
std::forward<Args>(args),
std::get<Idx>(init_)...,
std::forward<Inits>(inits)...);
}
template<std::size_t ... Idx, typename Executor, typename Args, typename ... Inits>
auto invoke(detail::index_sequence<Idx...>,
Executor exec,
error_code & ec,
const typename std::enable_if<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor<Executor>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<Executor>
{
return launcher_(std::move(exec), ec,
executable,
std::forward<Args>(args),
std::get<Idx>(init_)...,
std::forward<Inits>(inits)...);
}
Launcher launcher_;
std::tuple<Init...> init_;
};
template<typename Launcher, typename ... Init>
auto bind_launcher(Launcher && launcher, Init && ... init)
-> bound_launcher<typename std::decay<Launcher>::type,
typename std::decay<Init>::type...>
{
return bound_launcher<typename std::decay<Launcher>::type,
typename std::decay<Init>::type...>(
std::forward<Launcher>(launcher),
std::forward<Init>(init)...);
}
/// @brief @overload bind_launcher(Launcher && launcher, Init && init)
/// @tparam ...Init The initializer types to bind to the default_launcher.
/// @param ...init The initializers types to bind to the default_launcher.
/// @return The new default_launcher.
template<typename ... Init>
auto bind_default_launcher(Init && ... init)
-> bound_launcher<default_process_launcher,
typename std::decay<Init>::type...>
{
return bound_launcher<default_process_launcher,
typename std::decay<Init>::type...>(
default_process_launcher(),
std::forward<Init>(init)...);
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_BIND_LAUNCHER_HPP

View File

@@ -7,18 +7,19 @@
#if defined(BOOST_PROCESS_V2_STANDALONE) #if defined(BOOST_PROCESS_V2_STANDALONE)
#define BOOST_PROCESS_V2_ASIO_NAMESPACE ::asio #define BOOST_PROCESS_V2_ASIO_NAMESPACE asio
#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) ASIO_COMPLETION_TOKEN_FOR(Sig) #define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) ASIO_COMPLETION_TOKEN_FOR(Sig)
#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) #define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)
#define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature) #define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature)
#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) ASIO_DEFAULT_COMPLETION_TOKEN(Executor) #define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) ASIO_DEFAULT_COMPLETION_TOKEN(Executor)
#define BOOST_PROCESS_V2_INITFN_DEDUCED_RESULT_TYPE(x,y,z) ASIO_INITFN_DEDUCED_RESULT_TYPE(x,y,z)
#include <asio/detail/config.hpp> #include <asio/detail/config.hpp>
#include <system_error> #include <system_error>
#include <filesystem> #include <filesystem>
#include <string_view> #include <string_view>
#include <iomanip> #include <iomanip>
#include <optional>
#if defined(ASIO_WINDOWS) #if defined(ASIO_WINDOWS)
#define BOOST_PROCESS_V2_WINDOWS 1 #define BOOST_PROCESS_V2_WINDOWS 1
@@ -39,18 +40,19 @@
#else #else
#define BOOST_PROCESS_V2_ASIO_NAMESPACE ::boost::asio #define BOOST_PROCESS_V2_ASIO_NAMESPACE boost::asio
#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) BOOST_ASIO_COMPLETION_TOKEN_FOR(Sig) #define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) BOOST_ASIO_COMPLETION_TOKEN_FOR(Sig)
#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) #define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)
#define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature) #define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature)
#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor) #define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor)
#define BOOST_PROCESS_V2_INITFN_DEDUCED_RESULT_TYPE(x,y,z) BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(x,y,z)
#include <boost/config.hpp> #include <boost/config.hpp>
#include <boost/io/quoted.hpp> #include <boost/io/quoted.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <boost/system/system_category.hpp> #include <boost/system/system_category.hpp>
#include <boost/system/system_error.hpp> #include <boost/system/system_error.hpp>
#include <boost/optional.hpp>
#if defined(BOOST_WINDOWS_API) #if defined(BOOST_WINDOWS_API)
#define BOOST_PROCESS_V2_WINDOWS 1 #define BOOST_PROCESS_V2_WINDOWS 1
@@ -72,11 +74,9 @@
#if defined(BOOST_PROCESS_USE_STD_FS) #if defined(BOOST_PROCESS_USE_STD_FS)
#include <filesystem> #include <filesystem>
#include <optional>
#else #else
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/optional.hpp>
#endif #endif
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace boost { namespace process { namespace v2 { #define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace boost { namespace process { namespace v2 {
@@ -142,7 +142,7 @@ BOOST_PROCESS_V2_END_NAMESPACE
#endif #endif
#endif #endif
#if defined(__FreeBSD__) && !defined(BOOST_PROCESS_V2_DISABLE_PDFORK) #if defined(__FreeBSD__) && defined(BOOST_PROCESS_V2_ENABLE_PDFORK)
#define BOOST_PROCESS_V2_PDFORK 1 #define BOOST_PROCESS_V2_PDFORK 1
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1 #define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
#endif #endif

View File

@@ -77,14 +77,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)

View File

@@ -88,16 +88,15 @@ 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 +165,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);
} }

View File

@@ -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);
if (ec) for (wcstring_ref cs : std::forward<Args>(args))
return; res.insert(res.end(), cs.begin(), std::next(cs.end()));
length ++ ;
unicode_env.resize(length);
auto itr = &unicode_env.front(); res.push_back(L'\0');
for (string_view v : args) return res;
{
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 &);

View File

@@ -12,6 +12,15 @@
#define BOOST_PROCESS_V2_EXIT_CODE_HPP #define BOOST_PROCESS_V2_EXIT_CODE_HPP
#include <boost/process/v2/detail/config.hpp> #include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/error.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/associator.hpp>
#include <asio/async_result.hpp>
#else
#include <boost/asio/associator.hpp>
#include <boost/asio/async_result.hpp>
#endif
#if defined(BOOST_PROCESS_V2_POSIX) #if defined(BOOST_PROCESS_V2_POSIX)
#include <sys/wait.h> #include <sys/wait.h>
@@ -85,6 +94,157 @@ inline int evaluate_exit_code(int code)
#endif #endif
/** Convert the exit-code in a completion into an error if the actual error isn't set.
* @code {.cpp}
* process proc{ctx, "exit", {"1"}};
*
* proc.async_wait(code_as_error(
* [](error_code ec)
* {
* assert(ec.value() == 10);
* assert(ec.category() == error::get_exit_code_category());
* }));
*
* @endcode
*/
template<typename CompletionToken>
struct code_as_error_t
{
CompletionToken token_;
const error_category & category;
template<typename Token_>
code_as_error_t(Token_ && token, const error_category & category)
: token_(std::forward<Token_>(token)), category(category)
{
}
};
/// Deduction function for code_as_error_t.
template<typename CompletionToken>
code_as_error_t<CompletionToken> code_as_error(
CompletionToken && token,
const error_category & category = error::get_exit_code_category())
{
return code_as_error_t<typename std::decay<CompletionToken>::type>(
std::forward<CompletionToken>(token), category);
};
namespace detail
{
template<typename Handler>
struct code_as_error_handler
{
typedef void result_type;
template<typename H>
code_as_error_handler(H && h, const error_category & category)
: handler_(std::forward<H>(h)), category(category)
{
}
void operator()(error_code ec, native_exit_code_type code)
{
if (!ec)
ec.assign(code, category);
std::move(handler_)(ec);
}
Handler handler_;
const error_category & category;
};
}
BOOST_PROCESS_V2_END_NAMESPACE BOOST_PROCESS_V2_END_NAMESPACE
#if !defined(BOOST_PROCESS_V2_STANDALONE)
namespace boost
{
#endif
namespace asio
{
template <typename CompletionToken>
struct async_result<
BOOST_PROCESS_V2_NAMESPACE::code_as_error_t<CompletionToken>,
void(BOOST_PROCESS_V2_NAMESPACE::error_code,
BOOST_PROCESS_V2_NAMESPACE::native_exit_code_type)>
{
using signature = void(BOOST_PROCESS_V2_NAMESPACE::error_code);
using return_type = typename async_result<CompletionToken, void(BOOST_PROCESS_V2_NAMESPACE::error_code)>::return_type;
template <typename Initiation>
struct init_wrapper
{
init_wrapper(Initiation init)
: initiation_(std::move(init))
{
}
template <typename Handler, typename... Args>
void operator()(
Handler && handler,
const BOOST_PROCESS_V2_NAMESPACE::error_category & cat,
Args && ... args)
{
std::move(initiation_)(
BOOST_PROCESS_V2_NAMESPACE::detail::code_as_error_handler<typename decay<Handler>::type>(
std::forward<Handler>(handler), cat),
std::forward<Args>(args)...);
}
Initiation initiation_;
};
template <typename Initiation, typename RawCompletionToken, typename... Args>
static BOOST_PROCESS_V2_INITFN_DEDUCED_RESULT_TYPE(CompletionToken, signature,
(async_initiate<CompletionToken, signature>(
declval<init_wrapper<typename decay<Initiation>::type> >(),
declval<CompletionToken&>(),
declval<BOOST_ASIO_MOVE_ARG(Args)>()...)))
initiate(
Initiation && initiation,
RawCompletionToken && token,
Args &&... args)
{
return async_initiate<CompletionToken, signature>(
init_wrapper<typename decay<Initiation>::type>(
std::forward<Initiation>(initiation)),
token.token_,
token.category,
std::forward<Args>(args)...);
}
};
template<template <typename, typename> class Associator, typename Handler, typename DefaultCandidate>
struct associator<Associator,
BOOST_PROCESS_V2_NAMESPACE::detail::code_as_error_handler<Handler>, DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const BOOST_PROCESS_V2_NAMESPACE::detail::code_as_error_handler<Handler> & h,
const DefaultCandidate& c = DefaultCandidate()) noexcept
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
}
#if !defined(BOOST_PROCESS_V2_STANDALONE)
} // boost
#endif
#endif //BOOST_PROCESS_V2_EXIT_CODE_HPP #endif //BOOST_PROCESS_V2_EXIT_CODE_HPP

View 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

View File

@@ -74,8 +74,6 @@ struct basic_popen : basic_process<Executor>
{ {
} }
/// Construct a child from a property list and launch it using the default process launcher. /// Construct a child from a property list and launch it using the default process launcher.
template<typename ... Inits> template<typename ... Inits>
explicit basic_popen( explicit basic_popen(
@@ -85,30 +83,66 @@ struct basic_popen : basic_process<Executor>
Inits&&... inits) Inits&&... inits)
: basic_process<Executor>(executor) : basic_process<Executor>(executor)
{ {
*static_cast<basic_process<Executor>*>(this) = this->basic_process<Executor>::operator=(
default_process_launcher()( default_process_launcher()(
this->get_executor(), exe, args, this->get_executor(), exe, args,
std::forward<Inits>(inits)..., std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_} process_stdio{stdin_, stdout_}
); ));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
executor_type executor,
const filesystem::path& exe,
std::initializer_list<string_view> args,
Inits&&... inits)
: basic_process<Executor>(executor)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
} }
/// Construct a child from a property list and launch it using the default process launcher. /// Construct a child from a property list and launch it using the default process launcher.
template<typename ... Inits> template<typename ... Inits>
explicit basic_popen( explicit basic_popen(
executor_type executor, executor_type executor,
const filesystem::path& exe, const filesystem::path& exe,
std::initializer_list<wstring_view> args, std::initializer_list<wstring_view> args,
Inits&&... inits) Inits&&... inits)
: basic_process<Executor>(executor) : basic_process<Executor>(executor)
{ {
*static_cast<basic_process<Executor>*>(this) = this->basic_process<Executor>::operator=(
default_process_launcher()( default_process_launcher()(
this->get_executor(), exe, args, this->get_executor(), exe, args,
std::forward<Inits>(inits)..., std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_} process_stdio{stdin_, stdout_}
); ));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
executor_type executor,
const filesystem::path& exe,
std::initializer_list<wstring_view> args,
Inits&&... inits)
: basic_process<Executor>(executor)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
} }
/// Construct a child from a property list and launch it using the default process launcher. /// Construct a child from a property list and launch it using the default process launcher.
@@ -119,53 +153,112 @@ struct basic_popen : basic_process<Executor>
Args&& args, Inits&&... inits) Args&& args, Inits&&... inits)
: basic_process<Executor>(executor) : basic_process<Executor>(executor)
{ {
*static_cast<basic_process<Executor>*>(this) = this->basic_process<Executor>::operator=(
default_process_launcher()( default_process_launcher()(
std::move(executor), exe, args, std::move(executor), exe, args,
std::forward<Inits>(inits)..., std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_} process_stdio{stdin_, stdout_}
); ));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename Args, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
executor_type executor,
const filesystem::path& exe,
Args&& args, Inits&&... inits)
: basic_process<Executor>(executor)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
std::move(executor), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
} }
/// Construct a child from a property list and launch it using the default process launcher. /// Construct a child from a property list and launch it using the default process launcher.
template<typename ExecutionContext, typename ... Inits> template<typename ExecutionContext, typename ... Inits>
explicit basic_popen( explicit basic_popen(
ExecutionContext & context, ExecutionContext & context,
typename std::enable_if< typename std::enable_if<
std::is_convertible<ExecutionContext&, std::is_convertible<ExecutionContext&,
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
const filesystem::path&>::type exe, const filesystem::path&>::type exe,
std::initializer_list<string_view> args, std::initializer_list<string_view> args,
Inits&&... inits) Inits&&... inits)
: basic_process<Executor>(context) : basic_process<Executor>(context)
{ {
*static_cast<basic_process<Executor>*>(this) = this->basic_process<Executor>::operator=(
default_process_launcher()( default_process_launcher()(
this->get_executor(), exe, args, this->get_executor(), exe, args,
std::forward<Inits>(inits)..., std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_} process_stdio{stdin_, stdout_}
); ));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename ExecutionContext, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
ExecutionContext & context,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
const filesystem::path&>::type exe,
std::initializer_list<string_view> args,
Inits&&... inits)
: basic_process<Executor>(context)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
} }
/// Construct a child from a property list and launch it using the default process launcher. /// Construct a child from a property list and launch it using the default process launcher.
template<typename ExecutionContext, typename Args, typename ... Inits> template<typename ExecutionContext, typename Args, typename ... Inits>
explicit basic_popen( explicit basic_popen(
ExecutionContext & context, ExecutionContext & context,
typename std::enable_if< typename std::enable_if<
std::is_convertible<ExecutionContext&, std::is_convertible<ExecutionContext&,
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
const filesystem::path&>::type exe, const filesystem::path&>::type exe,
Args&& args, Inits&&... inits) Args&& args, Inits&&... inits)
: basic_process<Executor>(context) : basic_process<Executor>(context)
{ {
*static_cast<basic_process<Executor>*>(this) = this->basic_process<Executor>::operator=(
default_process_launcher()( default_process_launcher()(
this->get_executor(), exe, args, this->get_executor(), exe, args,
std::forward<Inits>(inits)..., std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_} process_stdio{stdin_, stdout_}
); ));
} }
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename ExecutionContext, typename Args, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
ExecutionContext & context,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
const filesystem::path&>::type exe,
Args&& args, Inits&&... inits)
: basic_process<Executor>(context)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
}
/// The type used for stdin on the parent process side. /// The type used for stdin on the parent process side.
using stdin_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor>; using stdin_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor>;
/// The type used for stdout on the parent process side. /// The type used for stdout on the parent process side.

View File

@@ -91,13 +91,17 @@ struct bind_fd
{ {
} }
error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
{
launcher.fd_whitelist.push_back(target);
return {};
}
/// 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 ();
} }
}; };

View File

@@ -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<

View File

@@ -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)

View File

@@ -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()) {}
@@ -339,7 +341,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));

View 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

View File

@@ -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

View File

@@ -11,6 +11,7 @@
#define BOOST_PROCESS_v2_START_DIR_HPP #define BOOST_PROCESS_v2_START_DIR_HPP
#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/default_launcher.hpp> #include <boost/process/v2/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE BOOST_PROCESS_V2_BEGIN_NAMESPACE

View File

@@ -11,8 +11,9 @@
#define BOOST_PROCESS_V2_STDIO_HPP #define BOOST_PROCESS_V2_STDIO_HPP
#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/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 +53,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 +63,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 +83,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 +103,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 +118,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 +295,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

View File

@@ -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;
}
}; };

View File

@@ -1,6 +1,6 @@
{ {
"key": "process", "key": "process",
"name": "process", "name": "Process",
"authors": [ "authors": [
"Merino Vidal", "Ilya Sokolov", "Felipe Tanus", "Merino Vidal", "Ilya Sokolov", "Felipe Tanus",
"Jeff Flinn", "Thomas Jarosch", "Boris Schaeling", "Klemens D. Morgenstern" "Jeff Flinn", "Thomas Jarosch", "Boris Schaeling", "Klemens D. Morgenstern"

View File

@@ -100,8 +100,8 @@ test-suite with-valgrind :
[ run env.cpp program_options system filesystem : [ test-options env ] : sparring_partner ] [ run env.cpp program_options system filesystem : [ test-options env ] : sparring_partner ]
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch ] [ run group.cpp system thread filesystem : [ test-options group ] : sub_launch ]
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch : <build>no <target-os>windows:<build>yes <define>BOOST_USE_WINDOWS_H=1 : group-windows-h ] [ run group.cpp system thread filesystem : [ test-options group ] : sub_launch : <build>no <target-os>windows:<build>yes <define>BOOST_USE_WINDOWS_H=1 : group-windows-h ]
[ run group_wait.cpp system thread filesystem : [ test-options group_wait ] : sparring_partner : <target-os>darwin:<build>no ] [ run group_wait.cpp system thread filesystem : [ test-options group_wait ] : sparring_partner : <target-os>darwin:<build>no <target-os>freebsd:<build>no ]
[ run limit_fd.cpp program_options system filesystem : [ test-options limit_fd ] : sparring_partner ] [ run limit_fd.cpp program_options system filesystem : [ test-options limit_fd ] : sparring_partner : <target-os>freebsd:<build>no ]
[ run run_exe.cpp filesystem : : sparring_partner ] [ run run_exe.cpp filesystem : : sparring_partner ]
[ run run_exe_path.cpp filesystem : [ test-options run_exe_path ] : sparring_partner ] [ run run_exe_path.cpp filesystem : [ test-options run_exe_path ] : sparring_partner ]
[ run search_path.cpp filesystem system : [ test-options search_path ] : : <target-os>windows:<source>shell32 ] [ run search_path.cpp filesystem system : [ test-options search_path ] : : <target-os>windows:<source>shell32 ]

View File

@@ -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());
}
}

View File

@@ -16,7 +16,7 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/scope_exit.hpp>
#include <boost/process/error.hpp> #include <boost/process/error.hpp>
#include <boost/process/io.hpp> #include <boost/process/io.hpp>
#include <boost/process/args.hpp> #include <boost/process/args.hpp>
@@ -45,6 +45,10 @@ BOOST_AUTO_TEST_CASE(wait_group_test, *boost::unit_test::timeout(5))
BOOST_REQUIRE(done.load()); BOOST_REQUIRE(done.load());
}}; }};
BOOST_SCOPE_EXIT_ALL(&) {
done.store(true);
thr.join();
};
using boost::unit_test::framework::master_test_suite; using boost::unit_test::framework::master_test_suite;
@@ -78,9 +82,6 @@ BOOST_AUTO_TEST_CASE(wait_group_test, *boost::unit_test::timeout(5))
BOOST_CHECK(!c1.running()); BOOST_CHECK(!c1.running());
BOOST_CHECK(!c2.running()); BOOST_CHECK(!c2.running());
done.store(true);
thr.join();
} }

View File

@@ -40,12 +40,19 @@ BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
{ {
using boost::unit_test::framework::master_test_suite; using boost::unit_test::framework::master_test_suite;
#if defined(BOOST_WINDOWS_API) #if defined(BOOST_WINDOWS_API)
const auto get_handle = [](FILE * f) {return reinterpret_cast<bt::native_handle_type>(_get_osfhandle(_fileno(f)));}; const auto get_handle = [](FILE * f) {return reinterpret_cast<bt::native_handle_type>(_get_osfhandle(_fileno(f)));};
const auto socket_to_handle = [](::boost::winapi::UINT_PTR_ sock){return reinterpret_cast<::boost::winapi::HANDLE_>(sock);}; const auto socket_to_handle = [](::boost::winapi::UINT_PTR_ sock){return reinterpret_cast<::boost::winapi::HANDLE_>(sock);};
#else #else
const auto get_handle = [](FILE * f) {return fileno(f);}; const auto get_handle = [](FILE * f) {return fileno(f);};
const auto socket_to_handle = [](int i){ return i;}; const auto socket_to_handle = [](int i){ return i;};
#if !defined(__linux__)
return ;
#endif
#endif #endif
std::error_code ec; std::error_code ec;

View File

@@ -4,6 +4,9 @@ add_library(boost_process_v2_test_impl test_impl.cpp)
target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process) target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process)
target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1) target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1)
if (WIN32)
target_compile_definitions(boost_process_v2_test_impl PUBLIC WIN32_LEAN_AND_MEAN=1)
endif()
function(boost_process_v2_standalone_test name) function(boost_process_v2_standalone_test name)
add_executable(boost_process_v2_${name} ${name}.cpp) add_executable(boost_process_v2_${name} ${name}.cpp)
@@ -15,6 +18,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)

View File

@@ -53,6 +53,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 :

View File

@@ -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

View File

@@ -24,6 +24,7 @@
#include <boost/process/v2/start_dir.hpp> #include <boost/process/v2/start_dir.hpp>
#include <boost/process/v2/execute.hpp> #include <boost/process/v2/execute.hpp>
#include <boost/process/v2/stdio.hpp> #include <boost/process/v2/stdio.hpp>
#include <boost/process/v2/bind_launcher.hpp>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/asio/io_context.hpp> #include <boost/asio/io_context.hpp>
@@ -159,11 +160,17 @@ BOOST_AUTO_TEST_CASE(request_exit)
auto sh = closable(); auto sh = closable();
BOOST_CHECK_MESSAGE(!sh.empty(), sh); BOOST_CHECK_MESSAGE(!sh.empty(), sh);
bpv::process proc(ctx, sh, {}
asio::readable_pipe rp{ctx};
asio::writable_pipe wp{ctx};
asio::connect_pipe(rp, wp);
bpv::process proc(ctx, sh, {}, bpv::process_stdio{wp}
#if defined(ASIO_WINDOWS) #if defined(ASIO_WINDOWS)
, 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 +195,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)
@@ -354,18 +363,18 @@ BOOST_AUTO_TEST_CASE(popen)
// default CWD // default CWD
bpv::popen proc(ctx, pth, {"echo"}); bpv::popen proc(/*bpv::default_process_launcher(), */ctx, pth, {"echo"});
asio::write(proc, asio::buffer("FOOBAR")); 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_MESSAGE(ec == asio::error::eof || ec == asio::error::broken_pipe, ec.message());
BOOST_CHECK_EQUAL(ec, asio::error::eof); BOOST_REQUIRE_GE(n, 1u);
// remove EOF // remove EOF
res.pop_back();
BOOST_CHECK_EQUAL(res, "FOOBAR"); BOOST_CHECK_EQUAL(res, "FOOBAR");
proc.wait(); proc.wait();
@@ -425,11 +434,14 @@ std::string read_env(const char * name, Inits && ... inits)
std::string out; std::string out;
bpv::error_code ec; bpv::error_code ec;
const auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); auto sz = asio::read(rp, asio::dynamic_buffer(out), ec);
while (ec == asio::error::interrupted)
sz += asio::read(rp, asio::dynamic_buffer(out), ec);
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 +461,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 +474,72 @@ 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_CASE(exit_code_as_error)
{
using boost::unit_test::framework::master_test_suite;
const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]);
asio::io_context ctx;
bpv::process proc1(ctx, pth, {"exit-code", "0"});
bpv::process proc2(ctx, pth, {"exit-code", "2"});
bpv::process proc3(ctx, pth, {"sleep", "2000"});
int called = 0;
proc3.terminate();
proc1.async_wait(bpv::code_as_error([&](bpv::error_code ec){called ++; BOOST_CHECK(!ec);}));
proc2.async_wait(bpv::code_as_error([&](bpv::error_code ec){called ++; BOOST_CHECK_MESSAGE(ec, ec.message());}));
proc3.async_wait(bpv::code_as_error([&](bpv::error_code ec){called ++; BOOST_CHECK_MESSAGE(ec, ec.message());}));
ctx.run();
BOOST_CHECK_EQUAL(called, 3);
}
BOOST_AUTO_TEST_CASE(bind_launcher)
{
using boost::unit_test::framework::master_test_suite;
const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]);
asio::io_context ctx;
asio::readable_pipe rp{ctx};
asio::writable_pipe wp{ctx};
asio::connect_pipe(rp, wp);
auto target = bpv::filesystem::canonical(bpv::filesystem::temp_directory_path());
auto l = bpv::bind_default_launcher(bpv::process_start_dir(target));
std::vector<std::string> args = {"print-cwd"};
// default CWD
bpv::process proc = l(ctx, pth, args, bpv::process_stdio{/*.in=*/{}, /*.out=*/wp});
wp.close();
std::string out;
bpv::error_code ec;
auto sz = asio::read(rp, asio::dynamic_buffer(out), ec);
while (ec == asio::error::interrupted)
sz += asio::read(rp, asio::dynamic_buffer(out), ec);
BOOST_CHECK(sz != 0);
BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message());
BOOST_CHECK_MESSAGE(bpv::filesystem::path(out) == target,
bpv::filesystem::path(out) << " != " << target);
proc.wait();
BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code() << " from " << proc.native_exit_code());
}
BOOST_AUTO_TEST_SUITE_END(); BOOST_AUTO_TEST_SUITE_END();

56
test/v2/shell.cpp Executable file
View 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 true";
#else
auto raw_shell = "cmd /c exit 0";
#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(), 0);
}

View File

@@ -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);
} }

View File

@@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE(wait_for)
BOOST_CHECK(!c.wait_for(std::chrono::milliseconds(200))); BOOST_CHECK(!c.wait_for(std::chrono::milliseconds(200)));
BOOST_CHECK( c.wait_for(std::chrono::milliseconds(1000))); BOOST_CHECK( c.wait_for(std::chrono::milliseconds(2000)));
auto timeout_t = std::chrono::system_clock::now(); auto timeout_t = std::chrono::system_clock::now();