2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 04:42:24 +00:00

Compare commits

...

86 Commits

Author SHA1 Message Date
Christian Eggers
406cd3ecf3 v2: sync initial value for exit code with v1
The initial value for the exit_code (0x7f) in v2 doesn't work with musl libc.
Here WIFSIGNALED(0x7f) expands to a non-zero value:

http://git.musl-libc.org/cgit/musl/tree/include/sys/wait.h#n54
2024-04-01 06:40:48 +08:00
Devon Morris
c6951ff773 fix call on moved variable in execute 2024-04-01 06:38:00 +08:00
mknaleczb
f2330c195a Changed return value from "" to string_type() 2024-04-01 06:36:48 +08:00
Samuel Venable
2ae279bd15 Add more error handling to bp2::ext cwd.ipp. (#354)
* Add more error handling to bp2::ext cwd.ipp.

* Remove redundant errno checks

these functions don't fail...
2024-04-01 06:35:17 +08:00
Christian Eggers
7a17af0f5c v2: fix closing of file descriptors
Fix off-by-one error. Currently, no handles are actually closed.
2024-03-28 21:57:13 +08:00
Christian Eggers
768944672f posix: pipe_out: fix merge conflict
In commit cbaa913e3d ("Merge branch 'develop' into limit_fd"), there
have been merge conflicts in two files. In pipe_out.hpp, a previous
commit from the "limit_fd" branch f8c0dd4da5 ("prototype for
limit_fd") had been eliminated during wrong conflict resolution.

The final result was, that file descriptors for stdout pipes were not
preserved when using limit_handles.

Example:

boost::asio::io_context io_context;
bp::async_pipe my_stdin(io_context);
bp::async_pipe my_stdout(io_context);
bp::child my_child("/usr/bin/echo", "Hello world",
    bp::std_in  < my_stdin,  // preserved by limit_handles
    bp::std_out > my_stdout, // closed by limit_handles
    bp::std_err > stderr,    // preserved by limit_handles
    bp::limit_handles)

Fixes: cbaa913e3d ("Merge branch 'develop' into limit_fd")
2024-03-28 21:56:58 +08:00
Klemens Morgenstern
46acb247f5 test include fix. 2024-03-28 11:05:33 +08:00
Klemens Morgenstern
08e3549713 #include vector fixes. 2024-03-28 08:30:08 +08:00
Klemens Morgenstern
029ad735fe added missing rename of variable. 2023-10-25 19:29:41 +08:00
Klemens Morgenstern
03a348ebdd Merge branch 'develop' of https://github.com/boostorg/process into develop 2023-10-25 19:06:26 +08:00
Klemens Morgenstern
f289f26c87 minor windows cleanup 2023-10-25 15:38:49 +08:00
Klemens Morgenstern
8d9aa1e31d incresed request_exit delay in windows process test. 2023-10-13 14:25:04 +08:00
Samuel Venable
1873f34435 Fix V2::EXT::CWD [SunOS] (#310)
* Fix V2::EXT::CWD [SunOS]

filesystem::canonical is basically the same thing as realpath on Unix-likes, which only resolves one symbolic link. If one symbolic link points to yet another symbolic link and so on and so forth, it will not resolve all symbolic links. It will only do one link for each call to canonical. On SunOS, unlike Linux, /proc/${pid}/cwd does not directly point to the literal current working directory of the given ${pid}. Instead, it will point to yet another symlink - /proc/${pid}/path/cwd which once you have followed that second link only then will you have the literal cwd path for the process id.
2023-10-12 21:43:59 +08:00
Ilia
5f795d9e62 Fix compilation for macOS 14 SDK
Fixes #342
2023-10-09 10:46:25 +08:00
Daniel Richard G
f17be678f2 fix group_wait test in cmake build. 2023-10-09 10:46:03 +08:00
AJIOB
b9fc531507 Code typos fix 2023-10-05 09:36:50 +08:00
Klemens Morgenstern
69c2c25729 added SIGTERM bit mask for freeBSD. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
8ab2332327 v2/env win test fix. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
ea69cda6d8 added can_interrupt check on windows to win tests. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
6b75b4039f increased timeout & added diagnostics. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
3c1beb40f6 windows move handle fix. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
e51970e3bb fixed windows interrupt & request_exit test. 2023-10-05 06:52:20 +08:00
Klemens
f3f8548dea allowing for SIGTERM in exit code on posix interrupts. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
4b7a00d4cf target: global timer workaround helper. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
c11f31d77e drone windows update. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
3769ec01f4 process native-exit code test. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
af47f4677c creation-flags fix. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
cf14d54343 pid no-access workarounds. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
b81cac8042 moved interrupt & request_exit into target. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
c92cce3652 cmd.ipp return fix. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
d270712fba link error fix. 2023-10-05 06:52:20 +08:00
Klemens Morgenstern
7b6b93691f fixed executor reset_cancellation_state.
Closes #338.
2023-10-05 06:52:20 +08:00
Shauren
507768e230 Fixed compile warning on msvc 2023-09-14 08:09:35 +08:00
SilverPlate3
502dc48753 Pass empty argument
closes #256
2023-08-14 17:47:17 +08:00
Ed Tanous
402acc151a Use boost::throw_exception
Using boost::throw_exception allows for modifications to these
exceptions on a per-application basis, including overriding with custom
implementations.

This also has the benefit of allowing compilation with -fno-exceptions
set, which should make this code more portable.
2023-08-14 17:41:37 +08:00
Roberto Rodriguez
0503b0997c Fix compilation with -Wall and -Werror 2023-08-14 17:41:00 +08:00
Klemens Morgenstern
8d372cb510 v2::environment link fixes 2023-08-14 17:28:54 +08:00
Klemens Morgenstern
bfb1ebb5bd Merge branch 'master' into develop
# Conflicts:
#	.github/workflows/ci.yml
#	include/boost/process/environment.hpp
#	include/boost/process/v2/exit_code.hpp
#	include/boost/process/v2/ext/cmd.hpp
#	include/boost/process/v2/ext/cwd.hpp
#	include/boost/process/v2/ext/env.hpp
#	include/boost/process/v2/ext/exe.hpp
#	test/v2/pid.cpp
#	test/v2/process.cpp
2023-06-28 20:30:30 +08:00
Klemens Morgenstern
c005adc8fc simplified stdio. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
5cab462710 added empty env var check to tests. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
ccd46dc692 added Bcrypt to gcc win builds. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
b3c8c3a8da fixed popen test. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
4dd6f28094 removed code_as_error. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
d73f228469 popen test adjustements. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
ccd1717588 test updates for CI 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
9a4aeab97e simplified terminate test. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
d66dce11bd ext/*.hpp order fix 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
fc38699a4b terminate test logs ec. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
a859c5151c removed posix-cmake-subdir from github actions. 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
8c2f403841 posix::basic_cmd handles empty cmd
closes #304.
2023-06-28 20:24:12 +08:00
Klemens Morgenstern
6fb2702a79 fixed unsigned & signed warnings
closes #301
2023-06-28 20:24:12 +08:00
Klemens Morgenstern
6cd4244f05 doc typo fixes 2023-06-28 20:24:12 +08:00
Klemens Morgenstern
0c42a58eac ~pipe_buf catches exceptions from overflow
Closes #111
2023-06-28 20:24:12 +08:00
Klemens Morgenstern
f269236d38 vfork launcher fixes
closes #314
2023-06-28 20:24:12 +08:00
Klemens Morgenstern
4b413d34f4 Remove initializer_list<wstring_view> constructors.
They are causing ambiguity and are not that useful.

Closes #313
2023-06-28 20:24:12 +08:00
Klemens Morgenstern
1403af769b process uses v2::detail::throw_error
closes #318
2023-06-28 20:24:12 +08:00
Klemens Morgenstern
964f6d3f7e pipe_* includes error_code
closes #316
2023-06-28 20:24:12 +08:00
Klemens Morgenstern
bccf42a3ec include fix for v2/environment.hpp. 2023-06-28 20:24:12 +08:00
nikola-sh
70c7ae694f Fix MSVC compile errors 2023-06-13 20:44:03 +08:00
Klemens Morgenstern
3211afda4a More typo fixes. 2023-02-22 13:04:13 +08:00
Klemens Morgenstern
d3f006acd4 Typo fixes. 2023-02-22 13:01:46 +08:00
Klemens Morgenstern
99633a6e42 handle_info include fix for handles.hpp. 2023-02-22 12:26:31 +08:00
Klemens Morgenstern
1e614ee43e Reduced amount of test & fixed path comparisons. 2023-02-22 00:54:17 +08:00
Klemens Morgenstern
0e3358705d pid_test fixes for osx. 2023-02-21 16:27:40 +08:00
Klemens Morgenstern
27a35f452d Updated pid test. 2023-02-21 16:27:40 +08:00
Klemens Morgenstern
8fff7283ed removed noexcept from env v2 2023-02-21 16:27:40 +08:00
Klemens Morgenstern
611dac143f sighchld service & test fixes. 2023-02-20 14:09:38 +08:00
Klemens Morgenstern
8d93576b94 close #296. 2023-02-20 14:09:38 +08:00
Klemens Morgenstern
f703845011 ec use locations. 2023-02-20 14:09:38 +08:00
Klemens Morgenstern
abd052e09f xproc fixes 2023-02-20 14:09:38 +08:00
Samuel Venable
a3304564c6 extern process management. 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
5865c6b449 switched to BOOST_DEPRECATED. 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
4c872c0a0d added clang 3.8. noexcept deduction. 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
feabbee098 disabled terminate test for freebsd. 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
a5b6e70c39 [drone] Removed mlocate dep. 2023-02-20 14:09:37 +08:00
Ivan Efimov
347fc68476 Fix string construction in native_environment_impl::get 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
61ff12c8da Deprecated wait_for & wait_until. 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
bd8e81153c Switched vector in list of sigclhd_service.
Closes #175
2023-02-20 14:09:37 +08:00
Gary Miguel
5fde6bec9f fix error message 2023-02-20 14:09:37 +08:00
Klemens
cf64f7dc6a ec fix for search_path with std::filesystem.
closes #287.
2023-02-20 14:09:37 +08:00
Orgad Shaneh
8355c3e1b6 Fix crash on search_path on Windows when PATHEXT is not found 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
de797e388d Update process.cpp 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
fc33435f8b Disabled some tests for freebsd & added interrupt handling to osx test. 2023-02-20 14:09:37 +08:00
sdarwin
f45ec624db Update metadata 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
9682056278 Typo fix. 2023-02-20 14:09:37 +08:00
Klemens Morgenstern
a00115b454 Include fixes. 2023-02-20 14:09:37 +08:00
60 changed files with 481 additions and 500 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -114,7 +114,6 @@ The async version supports cancellation and will forward cancellation types as f
if (!ec)
sig.emit(asio::cancellation_type::terminal);
});
);
});
```

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -66,7 +66,7 @@ namespace windows
{
if (wc == L'"')
*(itr++) = L'\\';
*(itr++) = wc;
*(itr++) = wc;
}
*(itr ++) = L'"';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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