mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
86 Commits
boost-1.82
...
boost-1.85
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
406cd3ecf3 | ||
|
|
c6951ff773 | ||
|
|
f2330c195a | ||
|
|
2ae279bd15 | ||
|
|
7a17af0f5c | ||
|
|
768944672f | ||
|
|
46acb247f5 | ||
|
|
08e3549713 | ||
|
|
029ad735fe | ||
|
|
03a348ebdd | ||
|
|
f289f26c87 | ||
|
|
8d9aa1e31d | ||
|
|
1873f34435 | ||
|
|
5f795d9e62 | ||
|
|
f17be678f2 | ||
|
|
b9fc531507 | ||
|
|
69c2c25729 | ||
|
|
8ab2332327 | ||
|
|
ea69cda6d8 | ||
|
|
6b75b4039f | ||
|
|
3c1beb40f6 | ||
|
|
e51970e3bb | ||
|
|
f3f8548dea | ||
|
|
4b7a00d4cf | ||
|
|
c11f31d77e | ||
|
|
3769ec01f4 | ||
|
|
af47f4677c | ||
|
|
cf14d54343 | ||
|
|
b81cac8042 | ||
|
|
c92cce3652 | ||
|
|
d270712fba | ||
|
|
7b6b93691f | ||
|
|
507768e230 | ||
|
|
502dc48753 | ||
|
|
402acc151a | ||
|
|
0503b0997c | ||
|
|
8d372cb510 | ||
|
|
bfb1ebb5bd | ||
|
|
c005adc8fc | ||
|
|
5cab462710 | ||
|
|
ccd46dc692 | ||
|
|
b3c8c3a8da | ||
|
|
4dd6f28094 | ||
|
|
d73f228469 | ||
|
|
ccd1717588 | ||
|
|
9a4aeab97e | ||
|
|
d66dce11bd | ||
|
|
fc38699a4b | ||
|
|
a859c5151c | ||
|
|
8c2f403841 | ||
|
|
6fb2702a79 | ||
|
|
6cd4244f05 | ||
|
|
0c42a58eac | ||
|
|
f269236d38 | ||
|
|
4b413d34f4 | ||
|
|
1403af769b | ||
|
|
964f6d3f7e | ||
|
|
bccf42a3ec | ||
|
|
70c7ae694f | ||
|
|
3211afda4a | ||
|
|
d3f006acd4 | ||
|
|
99633a6e42 | ||
|
|
1e614ee43e | ||
|
|
0e3358705d | ||
|
|
27a35f452d | ||
|
|
8fff7283ed | ||
|
|
611dac143f | ||
|
|
8d93576b94 | ||
|
|
f703845011 | ||
|
|
abd052e09f | ||
|
|
a3304564c6 | ||
|
|
5865c6b449 | ||
|
|
4c872c0a0d | ||
|
|
feabbee098 | ||
|
|
a5b6e70c39 | ||
|
|
347fc68476 | ||
|
|
61ff12c8da | ||
|
|
bd8e81153c | ||
|
|
5fde6bec9f | ||
|
|
cf64f7dc6a | ||
|
|
8355c3e1b6 | ||
|
|
de797e388d | ||
|
|
fc33435f8b | ||
|
|
f45ec624db | ||
|
|
9682056278 | ||
|
|
a00115b454 |
@@ -26,9 +26,7 @@ set B2_TARGETS=libs/!SELF!/test
|
||||
cd !BOOST_ROOT!
|
||||
call bootstrap.bat
|
||||
b2 headers
|
||||
b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% define=%DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/test -j3
|
||||
b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% define=%DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/example -j3
|
||||
|
||||
b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% define=%DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/test libs/!SELF!/example -j3
|
||||
) else if "%DRONE_JOB_BUILDTYPE%" == "standalone-windows" (
|
||||
|
||||
REM not used
|
||||
|
||||
49
.github/workflows/ci.yml
vendored
49
.github/workflows/ci.yml
vendored
@@ -199,52 +199,3 @@ jobs:
|
||||
run: |
|
||||
cd ../boost-root
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release embed-manifest-via=linker
|
||||
|
||||
posix-cmake-subdir:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
- os: macos-11
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
|
||||
- name: Use library with add_subdirectory
|
||||
run: |
|
||||
cd ../boost-root/libs/$LIBRARY/test/cmake_subdir_test
|
||||
mkdir __build__ && cd __build__
|
||||
cmake ..
|
||||
cmake --build .
|
||||
ctest --output-on-failure --no-tests=error
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ In this library the following functions are used for the creation of unnamed pip
|
||||
As the name suggests, named pipes have a string identifier. This means that a
|
||||
handle to them can be obtained with the identifier, too.
|
||||
|
||||
The implementation on posix uses [@(http://pubs.opengroup.org/onlinepubs/009695399/functions/mkfifo.html fifos],
|
||||
The implementation on posix uses [@http://pubs.opengroup.org/onlinepubs/009695399/functions/mkfifo.html fifos],
|
||||
which means, that the named pipe behaves like a file.
|
||||
|
||||
Windows does provide a facility called [@https://msdn.microsoft.com/en-us/library/windows/desktop/aa365150(v=vs.85).aspx named pipes],
|
||||
|
||||
@@ -83,7 +83,7 @@ will be called on the respective events on process launching. The names are:
|
||||
As an example:
|
||||
|
||||
```
|
||||
child c("ls", on_setup([](){cout << "On Setup" << endl;});
|
||||
child c("ls", on_setup([](){cout << "On Setup" << endl;}));
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ and use the following code, the `gcc` process will still run afterwards:
|
||||
|
||||
```
|
||||
bp::child c("make");
|
||||
if (!c.child_wait_for(std::chrono::seconds(10)) //give it 10 seconds
|
||||
if (!c.child_wait_for(std::chrono::seconds(10))) //give it 10 seconds
|
||||
c.child_terminate(); //then terminate
|
||||
```
|
||||
|
||||
@@ -357,7 +357,7 @@ So in order to also terminate `gcc` we can use a group.
|
||||
```
|
||||
bp::group g;
|
||||
bp::child c("make", g);
|
||||
if (!g.group_wait_for(std::chrono::seconds(10))
|
||||
if (!g.group_wait_for(std::chrono::seconds(10)))
|
||||
g.group_terminate();
|
||||
|
||||
c.child_wait(); //to avoid a zombie process & get the exit code
|
||||
|
||||
@@ -20,8 +20,8 @@ To note is the `find_executable` functions, which searches in an environment for
|
||||
|
||||
std::unordered_map<environment::key, environment::value> my_env =
|
||||
{
|
||||
{"SECRET", "THIS_IS_A_TEST"}
|
||||
{"PATH", {"/bin", "/usr/bin"}
|
||||
{"SECRET", "THIS_IS_A_TEST"},
|
||||
{"PATH", {"/bin", "/usr/bin"}}
|
||||
};
|
||||
|
||||
auto other_exe = environment::find_executable("g++", my_env);
|
||||
@@ -35,10 +35,10 @@ The subprocess environment assignment follows the same constraints:
|
||||
asio::io_context ctx;
|
||||
std::unordered_map<environment::key, environment::value> my_env =
|
||||
{
|
||||
{"SECRET", "THIS_IS_A_TEST"}
|
||||
{"PATH", {"/bin", "/usr/bin"}
|
||||
{"SECRET", "THIS_IS_A_TEST"},
|
||||
{"PATH", {"/bin", "/usr/bin"}}
|
||||
};
|
||||
auto exe = find_executable("g++"), my_env);
|
||||
auto exe = find_executable("g++");
|
||||
process proc(ctx, exe, {"main.cpp"}, process_environment(my_env));
|
||||
process pro2(ctx, exe, {"test.cpp"}, process_environment(my_env));
|
||||
```
|
||||
|
||||
@@ -57,7 +57,7 @@ file-handles for the subprocess.
|
||||
|
||||
Certain parts of boost.process were not as reliable as they should've been.
|
||||
|
||||
This concerns especially the `wait_for`` and `wait_until` functions on the process.
|
||||
This concerns especially the `wait_for` and `wait_until` functions on the process.
|
||||
The latter are easy to do on windows, but posix does not provide an API for this.
|
||||
Thus the wait_for used signals or fork, which was all but safe.
|
||||
Since process v2 is based on asio and thus supports cancellation,
|
||||
|
||||
@@ -114,7 +114,6 @@ The async version supports cancellation and will forward cancellation types as f
|
||||
if (!ec)
|
||||
sig.emit(asio::cancellation_type::terminal);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#include <system_error>
|
||||
#include <boost/system/api_config.hpp>
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/process/exception.hpp>
|
||||
#include <boost/assert/source_location.hpp>
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
#include <errno.h>
|
||||
@@ -71,31 +73,33 @@ inline std::error_code get_last_error() noexcept
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void throw_last_error(const std::string & msg)
|
||||
inline void throw_last_error(const std::string & msg, boost::source_location const & loc = boost::source_location())
|
||||
{
|
||||
throw process_error(get_last_error(), msg);
|
||||
boost::throw_exception(process_error(get_last_error(), msg), loc);
|
||||
}
|
||||
|
||||
inline void throw_last_error(const char * msg)
|
||||
inline void throw_last_error(const char * msg, boost::source_location const & loc = boost::source_location())
|
||||
{
|
||||
throw process_error(get_last_error(), msg);
|
||||
boost::throw_exception(process_error(get_last_error(), msg), loc);
|
||||
}
|
||||
|
||||
inline void throw_last_error()
|
||||
inline void throw_last_error(boost::source_location const & loc = boost::source_location())
|
||||
{
|
||||
throw process_error(get_last_error());
|
||||
boost::throw_exception(process_error(get_last_error()), loc);
|
||||
}
|
||||
|
||||
inline void throw_error(const std::error_code& ec)
|
||||
inline void throw_error(const std::error_code& ec,
|
||||
boost::source_location const & loc = boost::source_location())
|
||||
{
|
||||
if (ec)
|
||||
throw process_error(ec);
|
||||
boost::throw_exception(process_error(ec), loc);
|
||||
}
|
||||
|
||||
inline void throw_error(const std::error_code& ec, const char* msg)
|
||||
inline void throw_error(const std::error_code& ec, const char* msg,
|
||||
boost::source_location const & loc = boost::source_location())
|
||||
{
|
||||
if (ec)
|
||||
throw process_error(ec, msg);
|
||||
boost::throw_exception(process_error(ec, msg), loc);
|
||||
}
|
||||
|
||||
template<typename Char> constexpr Char null_char();
|
||||
|
||||
@@ -71,7 +71,7 @@ struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
}
|
||||
|
||||
template <typename Executor>
|
||||
inline void on_success(Executor &exec)
|
||||
inline void on_success(Executor &)
|
||||
{
|
||||
auto pipe = this->pipe;
|
||||
boost::asio::async_read(*pipe, buf,
|
||||
|
||||
@@ -112,7 +112,10 @@ struct exe_cmd_init<char> : boost::process::detail::api::handler_base_ext
|
||||
{
|
||||
if (exe.empty()) //cmd style
|
||||
{
|
||||
exec.exe = args.front().c_str();
|
||||
if (args.empty())
|
||||
exec.exe = "";
|
||||
else
|
||||
exec.exe = args.front().c_str();
|
||||
exec.cmd_style = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -77,12 +77,9 @@ public:
|
||||
void assign_source(native_handle_type h) { _source = h;}
|
||||
void assign_sink (native_handle_type h) { _sink = h;}
|
||||
|
||||
|
||||
|
||||
|
||||
int_type write(const char_type * data, int_type count)
|
||||
{
|
||||
int_type write_len;
|
||||
ssize_t write_len;
|
||||
while ((write_len = ::write(_sink, data, count * sizeof(char_type))) == -1)
|
||||
{
|
||||
//Try again if interrupted
|
||||
@@ -90,19 +87,19 @@ public:
|
||||
if (err != EINTR)
|
||||
::boost::process::detail::throw_last_error();
|
||||
}
|
||||
return write_len;
|
||||
return static_cast<int_type>(write_len);
|
||||
}
|
||||
int_type read(char_type * data, int_type count)
|
||||
{
|
||||
int_type read_len;
|
||||
while ((read_len = static_cast<int_type>(::read(_source, data, count * sizeof(char_type)))) == -1)
|
||||
ssize_t read_len;
|
||||
while ((read_len = ::read(_source, data, count * sizeof(char_type))) == -1)
|
||||
{
|
||||
//Try again if interrupted
|
||||
auto err = errno;
|
||||
if (err != EINTR)
|
||||
::boost::process::detail::throw_last_error();
|
||||
}
|
||||
return read_len;
|
||||
return static_cast<int_type>(read_len);
|
||||
}
|
||||
|
||||
bool is_open() const
|
||||
|
||||
@@ -33,7 +33,7 @@ inline std::vector<native_handle_type> get_handles(std::error_code & ec)
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
auto my_fd = ::dirfd(dir.get());
|
||||
auto my_fd = dirfd(dir.get());
|
||||
|
||||
struct ::dirent * ent_p;
|
||||
|
||||
@@ -117,7 +117,7 @@ struct limit_handles_ : handler_base_ext
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_fd = ::dirfd(dir);
|
||||
auto my_fd = dirfd(dir);
|
||||
struct ::dirent * ent_p;
|
||||
|
||||
while ((ent_p = readdir(dir)) != nullptr)
|
||||
|
||||
@@ -14,15 +14,25 @@
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct pipe_out : handler_base_ext
|
||||
struct pipe_out : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
int sink;
|
||||
int source; //opposite end
|
||||
|
||||
std::array<int, 4> get_used_handles()
|
||||
{
|
||||
const auto pp1 = p1 != -1 ? p1 : p2;
|
||||
const auto pp2 = p2 != -1 ? p2 : p1;
|
||||
|
||||
return {source, sink, pp1, pp2};
|
||||
}
|
||||
|
||||
pipe_out(int sink, int source) : sink(sink), source(source) {}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -61,6 +61,10 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
|
||||
arg += '"';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = "\"\"";
|
||||
}
|
||||
|
||||
if (!st.empty())//first one does not need a preceding space
|
||||
st += ' ';
|
||||
@@ -105,6 +109,10 @@ inline std::wstring build_args(const std::wstring & exe, std::vector<std::wstrin
|
||||
arg += '"';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = L"\"\"";
|
||||
}
|
||||
|
||||
if (!st.empty())//first one does not need a preceding space
|
||||
st += L' ';
|
||||
|
||||
@@ -158,7 +158,7 @@ basic_pipe<Char, Traits>::basic_pipe(const std::string & name)
|
||||
::boost::process::detail::throw_last_error("create_named_pipe() failed");
|
||||
|
||||
::boost::winapi::HANDLE_ sink = boost::winapi::create_file(
|
||||
name.c_str(),
|
||||
name_.c_str(),
|
||||
::boost::winapi::GENERIC_WRITE_, 0, nullptr,
|
||||
OPEN_EXISTING_,
|
||||
FILE_FLAG_OVERLAPPED_, //to allow read
|
||||
|
||||
@@ -65,7 +65,7 @@ inline auto native_environment_impl<Char>::get(const pointer_type id) -> string_
|
||||
{
|
||||
auto err = ::boost::winapi::GetLastError();
|
||||
if (err == ::boost::winapi::ERROR_ENVVAR_NOT_FOUND_)//well, then we consider that an empty value
|
||||
return "";
|
||||
return string_type();
|
||||
else
|
||||
throw process_error(std::error_code(err, std::system_category()),
|
||||
"GetEnvironmentVariable() failed");
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
|
||||
|
||||
template<int p1, int p2>
|
||||
struct pipe_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
|
||||
@@ -263,7 +263,7 @@ public:
|
||||
auto st1 = key + ::boost::process::detail::equal_sign<Char>();
|
||||
while (*p != nullptr)
|
||||
{
|
||||
const int len = std::char_traits<Char>::length(*p);
|
||||
const std::size_t len = std::char_traits<Char>::length(*p);
|
||||
if ((std::distance(st1.begin(), st1.end()) < len)
|
||||
&& std::equal(st1.begin(), st1.end(), *p))
|
||||
break;
|
||||
@@ -277,7 +277,7 @@ public:
|
||||
auto st1 = key + ::boost::process::detail::equal_sign<Char>();
|
||||
while (*p != nullptr)
|
||||
{
|
||||
const int len = std::char_traits<Char>::length(*p);
|
||||
const std::size_t len = std::char_traits<Char>::length(*p);
|
||||
if ((std::distance(st1.begin(), st1.end()) < len)
|
||||
&& std::equal(st1.begin(), st1.end(), *p))
|
||||
break;
|
||||
@@ -292,8 +292,9 @@ public:
|
||||
auto st1 = st + ::boost::process::detail::equal_sign<Char>();
|
||||
while (*p != nullptr)
|
||||
{
|
||||
const int len = std::char_traits<Char>::length(*p);
|
||||
if ((std::distance(st1.begin(), st1.end()) < len)
|
||||
const std::size_t len = std::char_traits<Char>::length(*p);
|
||||
if ((std::distance(st1.begin(), st1.end()) <
|
||||
static_cast<typename string_type::iterator::difference_type>(len))
|
||||
&& std::equal(st1.begin(), st1.end(), *p))
|
||||
return 1u;
|
||||
p++;
|
||||
|
||||
@@ -123,8 +123,14 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
///Destructor -> writes the frest of the data
|
||||
~basic_pipebuf()
|
||||
{
|
||||
if (basic_pipebuf::is_open())
|
||||
basic_pipebuf::overflow(Traits::eof());
|
||||
try
|
||||
{
|
||||
if (basic_pipebuf::is_open())
|
||||
basic_pipebuf::overflow(Traits::eof());
|
||||
}
|
||||
catch (process_error & )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
///Move construct from a pipe.
|
||||
|
||||
@@ -84,22 +84,32 @@ struct basic_process_handle_win
|
||||
|
||||
|
||||
template<typename Executor1>
|
||||
basic_process_handle_win(basic_process_handle_win<Executor1> && handle)
|
||||
: pid_(handle.pid_), handle_(handle.handle_.get_executor())
|
||||
basic_process_handle_win(basic_process_handle_win<Executor1> && other)
|
||||
: pid_(other.pid_), handle_(std::move(other.handle_))
|
||||
{
|
||||
other.pid_ = static_cast<DWORD>(-1);
|
||||
}
|
||||
|
||||
basic_process_handle_win(basic_process_handle_win && handle)
|
||||
: pid_(handle.id()), handle_(std::move(handle.handle_))
|
||||
basic_process_handle_win(basic_process_handle_win && other)
|
||||
: pid_(other.pid_), handle_(std::move(other.handle_))
|
||||
{
|
||||
handle.pid_ = static_cast<DWORD>(-1);
|
||||
other.pid_ = static_cast<DWORD>(-1);
|
||||
}
|
||||
|
||||
basic_process_handle_win& operator=(basic_process_handle_win && handle)
|
||||
basic_process_handle_win& operator=(basic_process_handle_win && other)
|
||||
{
|
||||
pid_ = handle.pid_;
|
||||
handle_ = std::move(handle.handle_);
|
||||
handle.pid_ = static_cast<DWORD>(-1);
|
||||
pid_ = other.pid_;
|
||||
handle_ = std::move(other.handle_);
|
||||
other.pid_ = static_cast<DWORD>(-1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Executor1>
|
||||
basic_process_handle_win& operator=(basic_process_handle_win<Executor1> && other)
|
||||
{
|
||||
pid_ = other.pid_;
|
||||
handle_ = std::move(other.handle_);
|
||||
other.pid_ = static_cast<DWORD>(-1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,13 @@
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
#include <boost/process/v2/detail/utf8.hpp>
|
||||
|
||||
#include <boost/type_traits.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#if !defined(GENERATING_DOCUMENTATION)
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
@@ -501,8 +505,8 @@ struct key
|
||||
|
||||
template< class Source >
|
||||
key( const Source& source,
|
||||
decltype(source.data()) = nullptr,
|
||||
decltype(source.size()) = 0u)
|
||||
decltype(std::declval<Source>().data()) = nullptr,
|
||||
decltype(std::declval<Source>().size()) = 0u)
|
||||
: value_(
|
||||
BOOST_PROCESS_V2_NAMESPACE::detail::conv_string<char_type, traits_type>(
|
||||
source.data(), source.size()))
|
||||
@@ -724,8 +728,8 @@ struct value
|
||||
|
||||
template< class Source >
|
||||
value( const Source& source,
|
||||
decltype(source.data()) = nullptr,
|
||||
decltype(source.size()) = 0u)
|
||||
decltype(std::declval<Source>().data()) = nullptr,
|
||||
decltype(std::declval<Source>().size()) = 0u)
|
||||
: value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string<char_type, traits_type>(
|
||||
source.data(), source.size()))
|
||||
{
|
||||
@@ -974,8 +978,8 @@ struct key_value_pair
|
||||
|
||||
template< class Source >
|
||||
key_value_pair( const Source& source,
|
||||
decltype(source.data()) = nullptr,
|
||||
decltype(source.size()) = 0u)
|
||||
decltype(std::declval<Source>().data()) = nullptr,
|
||||
decltype(std::declval<Source>().size()) = 0u)
|
||||
: value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string<char_type, traits_type>(
|
||||
source.data(), source.size()))
|
||||
{
|
||||
@@ -1743,8 +1747,8 @@ struct process_environment
|
||||
return build_env(env_buffer);
|
||||
}
|
||||
|
||||
process_environment(std::initializer_list<string_view> sv) : unicode_env{build_env(sv, "")} {}
|
||||
process_environment(std::initializer_list<wstring_view> sv) : unicode_env{build_env(sv, L"")} {}
|
||||
process_environment(std::initializer_list<string_view> sv) : unicode_env{build_env(sv)} {}
|
||||
process_environment(std::initializer_list<wstring_view> sv) : unicode_env{build_env(sv)} {}
|
||||
|
||||
template<typename Args>
|
||||
process_environment(Args && args) : unicode_env{build_env(std::forward<Args>(args))}
|
||||
@@ -1756,6 +1760,7 @@ struct process_environment
|
||||
std::vector<environment::key_value_pair> env_buffer;
|
||||
std::vector<wchar_t> unicode_env;
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
error_code on_setup(windows::default_launcher & launcher,
|
||||
const filesystem::path &, const std::wstring &);
|
||||
|
||||
@@ -1804,6 +1809,7 @@ struct process_environment
|
||||
}
|
||||
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
error_code on_setup(posix::default_launcher & launcher,
|
||||
const filesystem::path &, const char * const *);
|
||||
|
||||
@@ -1885,6 +1891,7 @@ struct hash<BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
|
||||
#include <boost/process/v2/impl/environment.ipp>
|
||||
#include <boost/process/v2/detail/impl/environment.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -66,7 +66,7 @@ struct execute_op
|
||||
template<typename Self>
|
||||
void operator()(Self && self)
|
||||
{
|
||||
self.reset_cancellation_state();
|
||||
self.reset_cancellation_state(BOOST_PROCESS_V2_ASIO_NAMESPACE::enable_total_cancellation());
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_slot s = self.get_cancellation_state().slot();
|
||||
if (s.is_connected())
|
||||
s.emplace<cancel>(proc.get());
|
||||
@@ -110,7 +110,7 @@ async_execute(basic_process<Executor> proc,
|
||||
WaitHandler && handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
|
||||
{
|
||||
std::unique_ptr<basic_process<Executor>> pro_(new basic_process<Executor>(std::move(proc)));
|
||||
auto exec = proc.get_executor();
|
||||
auto exec = pro_->get_executor();
|
||||
return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<WaitHandler, void(error_code, int)>(
|
||||
detail::execute_op<Executor>{std::move(pro_)}, handler, exec);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,11 @@ typedef int native_exit_code_type;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
constexpr native_exit_code_type still_active = 0x7f;
|
||||
constexpr native_exit_code_type still_active = 0x17f;
|
||||
static_assert(WIFSTOPPED(still_active), "Expected still_active to indicate WIFSTOPPED");
|
||||
static_assert(!WIFEXITED(still_active), "Expected still_active to not indicate WIFEXITED");
|
||||
static_assert(!WIFSIGNALED(still_active), "Expected still_active to not indicate WIFSIGNALED");
|
||||
static_assert(!WIFCONTINUED(still_active), "Expected still_active to not indicate WIFCONTINUED");
|
||||
}
|
||||
|
||||
inline bool process_is_running(int code)
|
||||
@@ -94,12 +98,19 @@ inline int evaluate_exit_code(int code)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/** Convert the exit-code in a completion into an error if the actual error isn't set.
|
||||
/// @{
|
||||
/** Helper to subsume an exit-code into an error_code if there's no actual error isn't set.
|
||||
* @code {.cpp}
|
||||
* process proc{ctx, "exit", {"1"}};
|
||||
*
|
||||
* proc.async_wait(code_as_error(
|
||||
* proc.async_wait(
|
||||
* asio::deferred(
|
||||
* [&proc](error_code ec, int)
|
||||
* {
|
||||
* return asio::deferred.values(
|
||||
* check_exit_code(ec, proc.native_exit_code())
|
||||
* );
|
||||
*
|
||||
* [](error_code ec)
|
||||
* {
|
||||
* assert(ec.value() == 10);
|
||||
@@ -107,144 +118,19 @@ inline int evaluate_exit_code(int code)
|
||||
* }));
|
||||
*
|
||||
* @endcode
|
||||
*/
|
||||
template<typename CompletionToken>
|
||||
struct code_as_error_t
|
||||
*/
|
||||
|
||||
inline error_code check_exit_code(
|
||||
error_code &ec, native_exit_code_type native_code,
|
||||
const error_category & category = error::get_exit_code_category())
|
||||
{
|
||||
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)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, code, category)
|
||||
std::move(handler_)(ec);
|
||||
}
|
||||
|
||||
|
||||
Handler handler_;
|
||||
const error_category & category;
|
||||
};
|
||||
|
||||
if (!ec)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, native_code, category);
|
||||
return ec;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
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
|
||||
@@ -20,6 +20,11 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace ext {
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_PROCESS_V2_DECL shell cmd(HANDLE handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL shell cmd(HANDLE handle);
|
||||
#endif
|
||||
|
||||
/// @{
|
||||
/// Get the argument vector of another process
|
||||
BOOST_PROCESS_V2_DECL shell cmd(pid_type pid, error_code & ec);
|
||||
|
||||
@@ -15,6 +15,11 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace ext {
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_PROCESS_V2_DECL filesystem::path cwd(HANDLE handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL filesystem::path cwd(HANDLE handle);
|
||||
#endif
|
||||
|
||||
/// @{
|
||||
/// Obtain the current path of another process
|
||||
BOOST_PROCESS_V2_DECL filesystem::path cwd(pid_type pid, error_code & ec);
|
||||
@@ -42,12 +47,6 @@ BOOST_PROCESS_V2_DECL filesystem::path cwd(basic_process_handle<Executor> & hand
|
||||
|
||||
/// @}
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_PROCESS_V2_DECL filesystem::path cwd(HANDLE handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL filesystem::path cwd(HANDLE handle);
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace ext
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
@@ -109,6 +109,11 @@ struct env_view
|
||||
detail::ext::native_env_handle_deleter> handle_;
|
||||
};
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_PROCESS_V2_DECL env_view env(HANDLE handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL env_view env(HANDLE handle);
|
||||
#endif
|
||||
|
||||
/// @{
|
||||
/// Get the environment of another process.
|
||||
BOOST_PROCESS_V2_DECL env_view env(pid_type pid, error_code & ec);
|
||||
@@ -136,10 +141,6 @@ BOOST_PROCESS_V2_DECL env_view env(basic_process_handle<Executor> & handle)
|
||||
|
||||
/// @}
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_PROCESS_V2_DECL env_view env(HANDLE handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL env_view env(HANDLE handle);
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace ext
|
||||
|
||||
@@ -16,6 +16,11 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace ext {
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_PROCESS_V2_DECL filesystem::path exe(HANDLE handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL filesystem::path exe(HANDLE handle);
|
||||
#endif
|
||||
|
||||
/// @{
|
||||
/// Return the executable of another process by pid or handle.
|
||||
BOOST_PROCESS_V2_DECL filesystem::path exe(pid_type pid, error_code & ec);
|
||||
@@ -45,11 +50,6 @@ filesystem::path exe(basic_process_handle<Executor> & handle)
|
||||
|
||||
///@}
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_PROCESS_V2_DECL filesystem::path exe(HANDLE handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL filesystem::path exe(HANDLE handle);
|
||||
#endif
|
||||
|
||||
} // namespace ext
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
@@ -146,7 +146,10 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
};
|
||||
std::unique_ptr<void, del> proc{detail::ext::open_process_with_debug_privilege(pid, ec)};
|
||||
if (proc == nullptr)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return shell{};
|
||||
}
|
||||
else
|
||||
return cmd(proc.get(), ec);
|
||||
|
||||
|
||||
@@ -112,13 +112,19 @@ filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code
|
||||
|
||||
filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
#if (defined(__linux__) || defined(__ANDROID__))
|
||||
return filesystem::canonical(
|
||||
filesystem::path("/proc") / std::to_string(pid) / "cwd", ec);
|
||||
filesystem::path("/proc") / std::to_string(pid) / "cwd", ec
|
||||
);
|
||||
#elif defined(__sun)
|
||||
return fileystem::canonical(
|
||||
filesystem::path("/proc") / std::to_string(pid) / "path/cwd", ec
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__)
|
||||
|
||||
// FIXME: Add error handling.
|
||||
filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
filesystem::path path;
|
||||
@@ -132,9 +138,7 @@ filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code
|
||||
filestat *fst = nullptr;
|
||||
STAILQ_FOREACH(fst, head, next) {
|
||||
if (fst->fs_uflags & PS_FST_UFLAG_CDIR)
|
||||
{
|
||||
path = filesystem::canonical(fst->fs_path, ec);
|
||||
}
|
||||
}
|
||||
procstat_freefiles(proc_stat, head);
|
||||
}
|
||||
@@ -153,7 +157,6 @@ filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code
|
||||
|
||||
#elif defined(__DragonFly__)
|
||||
|
||||
// FIXME: Add error handling.
|
||||
filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
filesystem::path path;
|
||||
@@ -178,7 +181,8 @@ filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
pclose(fp);
|
||||
if (pclose(fp) == -1)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
|
||||
@@ -133,7 +133,7 @@ filesystem::path exe(boost::process::v2::pid_type pid, boost::system::error_code
|
||||
);
|
||||
#elif defined(__sun)
|
||||
return fileystem::canonical(
|
||||
filesystem::path("/proc") / std::to_string(pid) / "path/a.out"
|
||||
filesystem::path("/proc") / std::to_string(pid) / "path/a.out", ec
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ struct exit_code_category final : public error_category
|
||||
}
|
||||
std::string message(int status) const
|
||||
{
|
||||
switch (status)
|
||||
switch (evaluate_exit_code(status))
|
||||
{
|
||||
case v2::detail::still_active:
|
||||
return "still-active";
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(GENERATING_DOCUMENTATION)
|
||||
|
||||
@@ -109,42 +109,6 @@ struct basic_popen : basic_process<Executor>
|
||||
));
|
||||
}
|
||||
|
||||
/// Construct a child from a property list and launch it using the default process launcher.
|
||||
template<typename ... Inits>
|
||||
explicit basic_popen(
|
||||
executor_type executor,
|
||||
const filesystem::path& exe,
|
||||
std::initializer_list<wstring_view> args,
|
||||
Inits&&... inits)
|
||||
: basic_process<Executor>(executor)
|
||||
{
|
||||
this->basic_process<Executor>::operator=(
|
||||
default_process_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.
|
||||
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.
|
||||
template<typename Args, typename ... Inits>
|
||||
explicit basic_popen(
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
#include <boost/process/v2/posix/detail/close_handles.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/detail/utf8.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
@@ -313,7 +314,7 @@ struct default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -344,7 +345,7 @@ struct default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ void close_all(const std::vector<int> & whitelist, error_code & ec)
|
||||
idx++)
|
||||
{
|
||||
const auto mine = whitelist[idx];
|
||||
const auto next = whitelist[idx];
|
||||
const auto next = whitelist[idx + 1];
|
||||
if ((mine + 1) != next && (mine != next))
|
||||
{
|
||||
::close_range(mine + 1, next - 1, 0);
|
||||
@@ -132,7 +132,7 @@ void close_all(const std::vector<int> & whitelist, error_code & ec)
|
||||
idx++)
|
||||
{
|
||||
const auto mine = whitelist[idx];
|
||||
const auto next = whitelist[idx];
|
||||
const auto next = whitelist[idx + 1];
|
||||
if ((mine + 1) != next && (mine != next))
|
||||
{
|
||||
::close_range(mine + 1, next - 1, CLOSE_RANGE_UNSHARE);
|
||||
|
||||
@@ -29,7 +29,7 @@ struct fork_and_forget_launcher : default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "fork_and_forget_launcher");
|
||||
v2::detail::throw_error(ec, "fork_and_forget_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -60,7 +60,7 @@ struct fork_and_forget_launcher : default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "fork_and_forget_launcher");
|
||||
v2::detail::throw_error(ec, "fork_and_forget_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ struct pdfork_launcher : default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "pdfork_launcher");
|
||||
v2::detail::throw_error(ec, "pdfork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ struct pdfork_launcher : default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "pdfork_launcher");
|
||||
v2::detail::throw_error(ec, "pdfork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ struct vfork_launcher : default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ struct vfork_launcher : default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -101,7 +101,6 @@ struct vfork_launcher : default_launcher
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child);
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
close_all_fds(ec);
|
||||
@@ -110,7 +109,7 @@ struct vfork_launcher : default_launcher
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category())
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
|
||||
|
||||
@@ -93,17 +93,7 @@ struct basic_process
|
||||
: basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward<Inits>(inits)...))
|
||||
{
|
||||
}
|
||||
/// Construct a child from a property list and launch it using the default launcher..
|
||||
template<typename ... Inits>
|
||||
explicit basic_process(
|
||||
executor_type executor,
|
||||
const filesystem::path& exe,
|
||||
std::initializer_list<wstring_view> args,
|
||||
Inits&&... inits)
|
||||
: basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward<Inits>(inits)...))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Construct a child from a property list and launch it using the default launcher..
|
||||
template<typename Args, typename ... Inits>
|
||||
explicit basic_process(
|
||||
@@ -265,7 +255,7 @@ struct basic_process
|
||||
{
|
||||
error_code ec;
|
||||
if (running(ec))
|
||||
wait(ec);
|
||||
process_handle_.wait(exit_status_, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "wait failed");
|
||||
return exit_code();
|
||||
@@ -287,8 +277,9 @@ struct basic_process
|
||||
return boost::exchange(process_handle_, get_executor());
|
||||
#endif
|
||||
}
|
||||
// Get the native
|
||||
/// Get the native
|
||||
native_handle_type native_handle() {return process_handle_.native_handle(); }
|
||||
/// Return the evaluated exit_code.
|
||||
int exit_code() const
|
||||
{
|
||||
return evaluate_exit_code(exit_status_);
|
||||
@@ -334,7 +325,7 @@ struct basic_process
|
||||
/** Note that this might be a process that already exited.*/
|
||||
bool is_open() const { return process_handle_.is_open(); }
|
||||
|
||||
/// Asynchronously wait for the process to exit and deliver the portable exit-code in the completion handler.
|
||||
/// Asynchronously wait for the process to exit and deliver the native exit-code in the completion handler.
|
||||
template <BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, int))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, int))
|
||||
|
||||
@@ -103,9 +103,15 @@ struct process_io_binding
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & readable_pipe,
|
||||
typename std::enable_if<Target != STD_INPUT_HANDLE, Executor*>::type = 0)
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & pipe)
|
||||
{
|
||||
if (Target == STD_INPUT_HANDLE)
|
||||
{
|
||||
auto h_ = pipe.native_handle();
|
||||
h = std::unique_ptr<void, handle_closer>{h_, get_flags(h_)};
|
||||
return ;
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
||||
@@ -113,14 +119,19 @@ struct process_io_binding
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
h = std::unique_ptr<void, handle_closer>{p[1], true};
|
||||
readable_pipe.assign(p[0]);
|
||||
pipe.assign(p[0]);
|
||||
}
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & writable_pipe,
|
||||
typename std::enable_if<Target == STD_INPUT_HANDLE, Executor*>::type = 0)
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & pipe)
|
||||
{
|
||||
if (Target != STD_INPUT_HANDLE)
|
||||
{
|
||||
auto h_ = pipe.native_handle();
|
||||
h = std::unique_ptr<void, handle_closer>{h_, get_flags(h_)};
|
||||
return ;
|
||||
}
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
||||
@@ -128,7 +139,7 @@ struct process_io_binding
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
h = std::unique_ptr<void, handle_closer>{p[0], true};
|
||||
writable_pipe.assign(p[1]);
|
||||
pipe.assign(p[1]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -170,13 +181,19 @@ struct process_io_binding
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & readable_pipe,
|
||||
typename std::enable_if<Target != STDIN_FILENO, Executor*>::type = 0)
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & readable_pipe)
|
||||
{
|
||||
if (Target == STDIN_FILENO)
|
||||
{
|
||||
fd = readable_pipe.native_handle();
|
||||
return ;
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
||||
if (ec)
|
||||
return ;
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
fd = p[1];
|
||||
if (::fcntl(p[0], F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
@@ -189,13 +206,20 @@ struct process_io_binding
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & writable_pipe,
|
||||
typename std::enable_if<Target == STDIN_FILENO, Executor*>::type = 0)
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & writable_pipe)
|
||||
{
|
||||
|
||||
if (Target != STDIN_FILENO)
|
||||
{
|
||||
fd = writable_pipe.native_handle();
|
||||
return ;
|
||||
}
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
|
||||
if (ec)
|
||||
return ;
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
fd = p[0];
|
||||
if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ struct as_user_launcher : default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "as_user_launcher");
|
||||
v2::detail::throw_error(ec, "as_user_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ struct as_user_launcher : default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "as_user_launcher");
|
||||
detail::throw_error(ec, "as_user_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
@@ -31,14 +31,19 @@ struct process_creation_flags
|
||||
const filesystem::path &,
|
||||
const std::wstring &) const
|
||||
{
|
||||
launcher.startup_info.StartupInfo.dwFlags |= Flags;
|
||||
launcher.creation_flags |= Flags;
|
||||
return error_code {};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// A flag to create a new process group. Necessary to allow interrupts for the subprocess.
|
||||
constexpr static process_creation_flags<CREATE_NEW_PROCESS_GROUP> create_new_process_group;
|
||||
|
||||
constexpr static process_creation_flags<CREATE_BREAKAWAY_FROM_JOB> create_breakaway_from_job;
|
||||
constexpr static process_creation_flags<CREATE_NEW_CONSOLE> create_new_console;
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <boost/process/v2/cstring_ref.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/detail/utf8.hpp>
|
||||
#include <boost/process/v2/error.hpp>
|
||||
|
||||
@@ -244,7 +245,7 @@ struct default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -275,7 +276,7 @@ struct default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -311,7 +312,6 @@ struct default_launcher
|
||||
&startup_info.StartupInfo,
|
||||
&process_information);
|
||||
|
||||
auto ec__ = detail::get_last_error();
|
||||
if (ok == 0)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace windows
|
||||
{
|
||||
if (wc == L'"')
|
||||
*(itr++) = L'\\';
|
||||
*(itr++) = wc;
|
||||
*(itr++) = wc;
|
||||
}
|
||||
|
||||
*(itr ++) = L'"';
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace windows
|
||||
/// A windows launcher using CreateProcessWithLogon instead of CreateProcess
|
||||
struct with_logon_launcher : default_launcher
|
||||
{
|
||||
std::wstring username, domain, password;
|
||||
std::wstring username, password, domain;
|
||||
DWORD logon_flags{0u};
|
||||
|
||||
with_logon_launcher(std::wstring username = L"",
|
||||
@@ -72,7 +72,7 @@ struct with_logon_launcher : default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "with_logon_launcher");
|
||||
v2::detail::throw_error(ec, "with_logon_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ struct with_token_launcher : default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "with_token_launcher");
|
||||
v2::detail::throw_error(ec, "with_token_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ struct with_token_launcher : default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
asio::detail::throw_error(ec, "with_token_launcher");
|
||||
detail::throw_error(ec, "with_token_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ process_standalone_test(pipe)
|
||||
|
||||
function(process_sub_launch_test name )
|
||||
add_executable(boost_process_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread Boost::unit_test_framework)
|
||||
target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::scope_exit Boost::thread Boost::unit_test_framework)
|
||||
add_test(NAME boost_process_${name} COMMAND $<TARGET_FILE:boost_process_${name}> $<TARGET_FILE:boost_process_sub_launch> )
|
||||
endfunction()
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15))
|
||||
|
||||
bp::child c2(
|
||||
master_test_suite().argv[1],
|
||||
"--wait", "4",
|
||||
"--wait", "5",
|
||||
g,
|
||||
ec
|
||||
);
|
||||
|
||||
@@ -97,7 +97,8 @@ BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
|
||||
|
||||
#if defined( BOOST_WINDOWS_API )
|
||||
std::thread thr([]{});
|
||||
BOOST_CHECK(!bt::is_stream_handle(thr.native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(!bt::is_stream_handle(::GetCurrentProcess(), ec));
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
thr.join();
|
||||
#else
|
||||
# if defined(TFD_CLOEXEC) //check timer
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
#include <boost/process/filesystem.hpp>
|
||||
|
||||
#if !defined(BOOST_PROCESS_USE_STD_FS)
|
||||
#include <boost/filesystem/directory.hpp>
|
||||
#endif
|
||||
|
||||
|
||||
#include <system_error>
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
#if defined(BOOST_POSIX_API)
|
||||
# include <boost/lexical_cast.hpp>
|
||||
# include <boost/iostreams/device/file_descriptor.hpp>
|
||||
@@ -148,7 +149,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else if (vm["loop"].as<bool>())
|
||||
{
|
||||
while (true);
|
||||
while (true)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
else if (vm["abort"].as<bool>())
|
||||
{
|
||||
|
||||
@@ -24,15 +24,6 @@ namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(terminate_set_on_error, *boost::unit_test::timeout(5))
|
||||
{
|
||||
std::atomic<bool> done{false};
|
||||
std::thread thr{
|
||||
[&]
|
||||
{
|
||||
for (int i = 0; i < 50 && !done.load(); i++)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
BOOST_REQUIRE(done.load());
|
||||
}};
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
std::error_code ec;
|
||||
bp::child c(
|
||||
@@ -44,31 +35,14 @@ BOOST_AUTO_TEST_CASE(terminate_set_on_error, *boost::unit_test::timeout(5))
|
||||
|
||||
BOOST_CHECK(c.valid());
|
||||
BOOST_CHECK(c.running(ec));
|
||||
|
||||
BOOST_CHECK(!c.wait_for(std::chrono::milliseconds(100), ec));
|
||||
BOOST_CHECK(c.running(ec));
|
||||
BOOST_CHECK(c.valid());
|
||||
|
||||
c.terminate(ec);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
||||
BOOST_CHECK(!c.running(ec));
|
||||
BOOST_CHECK(!ec);
|
||||
|
||||
done.store(true);
|
||||
thr.join();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(terminate_throw_on_error, *boost::unit_test::timeout(5))
|
||||
{
|
||||
std::atomic<bool> done{false};
|
||||
std::thread thr{
|
||||
[&]
|
||||
{
|
||||
for (int i = 0; i < 50 && !done.load(); i++)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
BOOST_REQUIRE(done.load());
|
||||
}};
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::error_code ec;
|
||||
@@ -79,17 +53,10 @@ BOOST_AUTO_TEST_CASE(terminate_throw_on_error, *boost::unit_test::timeout(5))
|
||||
ec
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
BOOST_CHECK(c.valid());
|
||||
BOOST_CHECK(c.running());
|
||||
|
||||
BOOST_CHECK(!c.wait_for(std::chrono::milliseconds(100), ec));
|
||||
BOOST_CHECK(c.running(ec));
|
||||
BOOST_CHECK(c.valid());
|
||||
|
||||
c.terminate();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
||||
BOOST_CHECK(!c.running());
|
||||
|
||||
done.store(true);
|
||||
thr.join();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ if [ os.name ] = NT
|
||||
lib Advapi32 ;
|
||||
lib Ntdll ;
|
||||
lib user32 ;
|
||||
lib Bcrypt ;
|
||||
}
|
||||
|
||||
project : requirements
|
||||
@@ -29,6 +30,7 @@ project : requirements
|
||||
<target-os>bsd:<linkflags>-lkvm
|
||||
<os>NT,<toolset>cw:<library>ws2_32
|
||||
<os>NT,<toolset>gcc:<library>ws2_32
|
||||
<os>NT,<toolset>gcc:<library>Bcrypt
|
||||
<define>BOOST_PROCESS_V2_SEPARATE_COMPILATION=1
|
||||
;
|
||||
|
||||
|
||||
@@ -62,7 +62,8 @@ BOOST_AUTO_TEST_CASE(environment)
|
||||
ec.clear();
|
||||
|
||||
for (auto && ke : bpe::current())
|
||||
BOOST_CHECK_EQUAL(bpe::get(std::get<0>(ke)), std::get<1>(ke));
|
||||
if (!std::get<1>(ke).empty())
|
||||
BOOST_CHECK_EQUAL(bpe::get(std::get<0>(ke)), std::get<1>(ke));
|
||||
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
@@ -150,11 +151,14 @@ BOOST_AUTO_TEST_CASE(wenvironment)
|
||||
BOOST_CHECK(ec);
|
||||
|
||||
for (const auto ke : bpe::current())
|
||||
{
|
||||
std::string key = std::get<0>(ke).string();
|
||||
if (!std::get<1>(ke).empty())
|
||||
BOOST_CHECK_EQUAL(bpe::get(std::get<0>(ke)), std::get<1>(ke));
|
||||
}
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
BOOST_CHECK_EQUAL(bpe::key(L"FOO"), bpe::key_view(L"Foo"));
|
||||
BOOST_CHECK(bpe::key(L"FOO") == std::wstring(L"Foo"));
|
||||
BOOST_CHECK_EQUAL(bpe::key_value_pair(L"Foo=BAR"), bpe::key_value_pair_view(L"FOO=BAR"));
|
||||
BOOST_CHECK_EQUAL(bpe::key_value_pair(L"Foo=BAR"), bpe::key_value_pair(L"FOO=BAR"));
|
||||
BOOST_CHECK_EQUAL(bpe::key_value_pair_view(L"Foo=BAR"), bpe::key_value_pair_view(L"FOO=BAR"));
|
||||
|
||||
@@ -24,8 +24,6 @@ BOOST_AUTO_TEST_CASE(test_pid)
|
||||
BOOST_CHECK_GT(all.size(), 0u);
|
||||
BOOST_CHECK(itr != all.end());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(child_pid)
|
||||
@@ -34,20 +32,25 @@ BOOST_AUTO_TEST_CASE(child_pid)
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bp2::filesystem::absolute(master_test_suite().argv[1]);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
auto cs = bp2::child_pids(bp2::current_pid());
|
||||
boost::asio::io_context ctx;
|
||||
bp2::process proc(ctx, pth, {"sleep", "50000"});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
bp2::process proc(ctx, pth, {"loop"});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
auto c2 = bp2::child_pids(bp2::current_pid());
|
||||
BOOST_CHECK_LT(cs.size(), c2.size());
|
||||
BOOST_CHECK_LE(cs.size(), c2.size());
|
||||
BOOST_CHECK(std::find(cs.begin(), cs.end(), proc.id()) == cs.end());
|
||||
BOOST_CHECK(std::find(c2.begin(), c2.end(), proc.id()) != c2.end());
|
||||
proc.terminate();
|
||||
proc.wait();
|
||||
if (!c2.empty())
|
||||
BOOST_CHECK(std::find(c2.begin(), c2.end(), proc.id()) != c2.end());
|
||||
boost::system::error_code ec;
|
||||
proc.terminate(ec);
|
||||
if (ec)
|
||||
BOOST_CHECK(ec == boost::system::errc::permission_denied);
|
||||
else
|
||||
proc.wait();
|
||||
|
||||
auto c3 = bp2::child_pids(bp2::current_pid());
|
||||
BOOST_CHECK(std::find(c3.begin(), c3.end(), proc.id()) == c3.end());
|
||||
BOOST_CHECK_LT(c3.size(), c2.size());
|
||||
BOOST_CHECK_LE(c3.size(), c2.size());
|
||||
}
|
||||
@@ -26,12 +26,19 @@
|
||||
#include <boost/process/v2/stdio.hpp>
|
||||
#include <boost/process/v2/bind_launcher.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#include <boost/process/v2/windows/creation_flags.hpp>
|
||||
#include <boost/process/v2/windows/show_window.hpp>
|
||||
#endif
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/connect_pipe.hpp>
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/asio/readable_pipe.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/asio/writable_pipe.hpp>
|
||||
|
||||
@@ -41,35 +48,6 @@
|
||||
namespace bpv = boost::process::v2;
|
||||
namespace asio = boost::asio;
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
bpv::filesystem::path shell()
|
||||
{
|
||||
return bpv::environment::find_executable("cmd");
|
||||
}
|
||||
|
||||
bpv::filesystem::path closable()
|
||||
{
|
||||
return bpv::environment::find_executable("notepad");
|
||||
}
|
||||
|
||||
bpv::filesystem::path interruptable()
|
||||
{
|
||||
return bpv::environment::find_executable("cmd");
|
||||
}
|
||||
#else
|
||||
bpv::filesystem::path shell()
|
||||
{
|
||||
return bpv::environment::find_executable("sh");
|
||||
}
|
||||
bpv::filesystem::path closable()
|
||||
{
|
||||
return bpv::environment::find_executable("tee");
|
||||
}
|
||||
bpv::filesystem::path interruptable()
|
||||
{
|
||||
return bpv::environment::find_executable("tee");
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(with_target);
|
||||
|
||||
@@ -87,7 +65,6 @@ BOOST_AUTO_TEST_CASE(exit_code_sync)
|
||||
args[1] = "42";
|
||||
auto proc = bpv::default_process_launcher()(ctx, pth, args);
|
||||
BOOST_CHECK_EQUAL(proc.wait(), 42);
|
||||
printf("42: %d\n", proc.native_exit_code());
|
||||
|
||||
BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"sleep", "100"}).wait(), 0);
|
||||
BOOST_CHECK_EQUAL(bpv::execute(bpv::process(ctx, pth, {"sleep", "100"})), 0);
|
||||
@@ -145,52 +122,69 @@ BOOST_AUTO_TEST_CASE(exit_code_async)
|
||||
BOOST_AUTO_TEST_CASE(terminate)
|
||||
{
|
||||
asio::io_context ctx;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
auto sh = shell();
|
||||
|
||||
BOOST_CHECK_MESSAGE(!sh.empty(), sh);
|
||||
bpv::process proc(ctx, sh, {});
|
||||
|
||||
bpv::process proc(ctx, pth, {"sleep", "10"});
|
||||
proc.suspend();
|
||||
proc.resume();
|
||||
proc.terminate();
|
||||
proc.wait();
|
||||
BOOST_CHECK_NE(proc.exit_code(), 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(request_exit)
|
||||
{
|
||||
asio::io_context ctx;
|
||||
|
||||
auto sh = closable();
|
||||
BOOST_CHECK_MESSAGE(!sh.empty(), sh);
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
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)
|
||||
, asio::windows::show_window_minimized_not_active
|
||||
bpv::process proc(ctx, pth, {"sigterm"}
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
, bpv::windows::show_window_minimized_not_active
|
||||
, bpv::windows::create_new_console
|
||||
#endif
|
||||
);
|
||||
BOOST_CHECK(proc.running());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
proc.request_exit();
|
||||
proc.wait();
|
||||
BOOST_CHECK_EQUAL(proc.exit_code() & ~SIGTERM, 0);
|
||||
}
|
||||
|
||||
bool can_interrupt = true;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(interrupt)
|
||||
{
|
||||
asio::io_context ctx;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
auto sh = interruptable();
|
||||
BOOST_CHECK_MESSAGE(!sh.empty(), sh);
|
||||
bpv::process proc(ctx, sh, {}
|
||||
#if defined(ASIO_WINDOWS)
|
||||
, asio::windows::create_new_process_group
|
||||
|
||||
bpv::process proc(ctx, pth, {"sigint"}
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
, bpv::windows::create_new_process_group
|
||||
#endif
|
||||
);
|
||||
proc.interrupt();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
bpv::error_code ec;
|
||||
proc.interrupt(ec);
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
// the interrupt only works on console applications, so it may not work depending on the environment.
|
||||
if (ec.value() == ERROR_INVALID_FUNCTION)
|
||||
{
|
||||
can_interrupt = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.what());
|
||||
proc.wait();
|
||||
BOOST_CHECK_EQUAL(proc.exit_code() & ~SIGTERM, 0);
|
||||
}
|
||||
|
||||
void trim_end(std::string & str)
|
||||
@@ -371,27 +365,26 @@ BOOST_AUTO_TEST_CASE(popen)
|
||||
|
||||
asio::io_context ctx;
|
||||
|
||||
asio::readable_pipe rp{ctx};
|
||||
|
||||
|
||||
// default CWD
|
||||
bpv::popen proc(/*bpv::default_process_launcher(), */ctx, pth, {"echo"});
|
||||
|
||||
asio::write(proc, asio::buffer("FOOBAR"));
|
||||
BOOST_CHECK_EQUAL(asio::write(proc, asio::buffer("FOOBAR", 6)), 6);
|
||||
|
||||
proc.get_stdin().close();
|
||||
|
||||
std::string res;
|
||||
boost::system::error_code ec;
|
||||
std::size_t n = asio::read(proc, asio::dynamic_buffer(res), ec);
|
||||
while (ec == asio::error::interrupted)
|
||||
n += asio::read(rp, asio::dynamic_buffer(res), ec);
|
||||
n += asio::read(proc, asio::dynamic_buffer(res), ec);
|
||||
|
||||
BOOST_CHECK_MESSAGE(ec == asio::error::eof || ec == asio::error::broken_pipe, ec.message());
|
||||
BOOST_CHECK_MESSAGE(ec == asio::error::eof
|
||||
|| ec == asio::error::broken_pipe,
|
||||
ec.message());
|
||||
BOOST_REQUIRE_GE(n, 1u);
|
||||
// remove EOF
|
||||
res.pop_back();
|
||||
BOOST_CHECK_EQUAL(res, "FOOBAR");
|
||||
|
||||
proc.get_stdin().close();
|
||||
proc.wait();
|
||||
BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code());
|
||||
}
|
||||
@@ -520,13 +513,34 @@ BOOST_AUTO_TEST_CASE(exit_code_as_error)
|
||||
|
||||
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());}));
|
||||
|
||||
proc1.async_wait(
|
||||
[&](bpv::error_code ec, int)
|
||||
{
|
||||
called ++;
|
||||
bpv::check_exit_code(ec, proc1.native_exit_code());
|
||||
BOOST_CHECK(!ec);
|
||||
});
|
||||
|
||||
proc2.async_wait(
|
||||
[&](bpv::error_code ec, int)
|
||||
{
|
||||
called ++;
|
||||
bpv::check_exit_code(ec, proc2.native_exit_code());
|
||||
BOOST_CHECK_MESSAGE(ec, ec.message());
|
||||
});
|
||||
|
||||
proc3.async_wait(
|
||||
[&](bpv::error_code ec, int)
|
||||
{
|
||||
called ++;
|
||||
bpv::check_exit_code(ec, proc3.native_exit_code());
|
||||
BOOST_CHECK_MESSAGE(ec, ec.message());
|
||||
});
|
||||
|
||||
|
||||
ctx.run();
|
||||
BOOST_CHECK_EQUAL(called, 3);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bind_launcher)
|
||||
@@ -537,17 +551,13 @@ BOOST_AUTO_TEST_CASE(bind_launcher)
|
||||
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();
|
||||
bpv::process proc = l(ctx, pth, args, bpv::process_stdio{/*.in=*/{}, /*.out=*/rp});
|
||||
|
||||
std::string out;
|
||||
bpv::error_code ec;
|
||||
@@ -556,8 +566,8 @@ BOOST_AUTO_TEST_CASE(bind_launcher)
|
||||
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_REQUIRE(sz != 0);
|
||||
|
||||
if (out.back() != '/' && target.string().back() == '/')
|
||||
out += '/';
|
||||
@@ -569,5 +579,68 @@ BOOST_AUTO_TEST_CASE(bind_launcher)
|
||||
BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code() << " from " << proc.native_exit_code());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_interrupt)
|
||||
{
|
||||
if (!can_interrupt)
|
||||
return;
|
||||
|
||||
asio::io_context ctx;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
|
||||
bpv::process proc(ctx, pth, {"sigint"}
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
, bpv::windows::create_new_process_group
|
||||
#endif
|
||||
);
|
||||
|
||||
asio::steady_timer tim{ctx, std::chrono::milliseconds(200)};
|
||||
asio::cancellation_signal sig;
|
||||
|
||||
bpv::async_execute(std::move(proc),
|
||||
asio::bind_cancellation_slot(
|
||||
sig.slot(),
|
||||
[](boost::system::error_code ec, int res)
|
||||
{
|
||||
BOOST_CHECK(!ec);
|
||||
BOOST_CHECK_EQUAL(
|
||||
bpv::evaluate_exit_code(res) & ~SIGTERM, 0);
|
||||
}));
|
||||
|
||||
tim.async_wait([&](bpv::error_code ec) { sig.emit(asio::cancellation_type::total); });
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_request_exit)
|
||||
{
|
||||
asio::io_context ctx;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
bpv::process proc(ctx, pth, {"sigterm"}
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
, bpv::windows::show_window_minimized_not_active
|
||||
, bpv::windows::create_new_console
|
||||
#endif
|
||||
);
|
||||
|
||||
asio::steady_timer tim{ctx, std::chrono::milliseconds(250)};
|
||||
asio::cancellation_signal sig;
|
||||
|
||||
bpv::async_execute(std::move(proc),
|
||||
asio::bind_cancellation_slot(
|
||||
sig.slot(),
|
||||
[](boost::system::error_code ec, int res)
|
||||
{
|
||||
BOOST_CHECK(!ec);
|
||||
BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(res) & ~SIGTERM, 0);
|
||||
}));
|
||||
|
||||
tim.async_wait([&](bpv::error_code ec) { sig.emit(asio::cancellation_type::partial); });
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/process/v2/environment.hpp>
|
||||
|
||||
extern char **environ;
|
||||
@@ -71,15 +72,71 @@ int main(int argc, char * argv[])
|
||||
GetStartupInfo(&si);
|
||||
return static_cast<int>(si.wShowWindow);
|
||||
}
|
||||
else if (mode == "creation-flags")
|
||||
{
|
||||
STARTUPINFO si;
|
||||
GetStartupInfo(&si);
|
||||
return static_cast<int>(si.dwFlags);
|
||||
}
|
||||
#endif
|
||||
else if (mode == "sigterm")
|
||||
{
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::steady_timer tim{ctx, std::chrono::seconds(10)};
|
||||
static boost::asio::steady_timer * tim_p = &tim;
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
SetConsoleCtrlHandler(
|
||||
[](DWORD kind)
|
||||
{
|
||||
if (kind == CTRL_CLOSE_EVENT)
|
||||
{
|
||||
// windows doesn't like us doing antyhing else
|
||||
::exit(0);
|
||||
if (tim_p != nullptr)
|
||||
tim_p->cancel();
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}, TRUE);
|
||||
#else
|
||||
signal(SIGTERM, [](int) { if (tim_p != nullptr) tim_p->cancel();});
|
||||
#endif
|
||||
|
||||
boost::system::error_code ec;
|
||||
tim.async_wait([&](boost::system::error_code ec_) { ec = ec_; });
|
||||
ctx.run();
|
||||
tim_p = nullptr;
|
||||
return ec ? EXIT_SUCCESS : 32;
|
||||
}
|
||||
else if (mode == "sigint")
|
||||
{
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::steady_timer tim{ctx, std::chrono::seconds(10)};
|
||||
static boost::asio::steady_timer * tim_p = &tim;
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
SetConsoleCtrlHandler(NULL, FALSE);
|
||||
auto res = SetConsoleCtrlHandler(
|
||||
[](DWORD kind)
|
||||
{
|
||||
if (kind == CTRL_C_EVENT)
|
||||
{
|
||||
if (tim_p != nullptr)
|
||||
tim_p->cancel();
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}, TRUE);
|
||||
BOOST_ASSERT(res != FALSE);
|
||||
#else
|
||||
signal(SIGINT, [](int) { if (tim_p != nullptr) tim_p->cancel();});
|
||||
#endif
|
||||
|
||||
boost::system::error_code ec;
|
||||
tim.async_wait([&](boost::system::error_code ec_) { ec = ec_; });
|
||||
ctx.run();
|
||||
tim_p = nullptr;
|
||||
return ec ? EXIT_SUCCESS : 33;
|
||||
}
|
||||
else
|
||||
return 34;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -50,19 +50,6 @@ BOOST_AUTO_TEST_CASE(show_window)
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(creation_flags)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
asio::io_context ctx;
|
||||
bpv::process proc{ctx, pth, {"creation-flags"}};
|
||||
|
||||
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>()};
|
||||
BOOST_CHECK(proc.running());
|
||||
BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, STARTF_TITLEISAPPID);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(as_user_launcher)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user