mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
50 Commits
boost-1.85
...
origin/xpr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4ba43e970 | ||
|
|
19212708e5 | ||
|
|
42ba48f744 | ||
|
|
1e60fcd830 | ||
|
|
d6bcb0014a | ||
|
|
e5f0f245bd | ||
|
|
a4a3770501 | ||
|
|
1e5892b8fb | ||
|
|
85b29d6442 | ||
|
|
b9ee759365 | ||
|
|
f3b163a933 | ||
|
|
be2ae2c3cf | ||
|
|
b2f5ebc760 | ||
|
|
e34557faa0 | ||
|
|
a9f083f45c | ||
|
|
b5ab94c932 | ||
|
|
9ceda79fa2 | ||
|
|
68cbeae491 | ||
|
|
ae778f36a8 | ||
|
|
0671e4a133 | ||
|
|
cbf944c57b | ||
|
|
5f2b8c53f4 | ||
|
|
dac3614a38 | ||
|
|
2fc71ca0a3 | ||
|
|
404682d75d | ||
|
|
9fbbdc3421 | ||
|
|
3df0009a2f | ||
|
|
ecb384b253 | ||
|
|
05bce942c1 | ||
|
|
dcf5d8ce41 | ||
|
|
4209f8ee6e | ||
|
|
d9513269cc | ||
|
|
293f28dab6 | ||
|
|
fe1b629b5d | ||
|
|
278fa57214 | ||
|
|
1addfba12e | ||
|
|
4cc469b2a4 | ||
|
|
6e4d1e29d2 | ||
|
|
dada865fd0 | ||
|
|
380dd1b00f | ||
|
|
7832cb6af3 | ||
|
|
68f4c50be9 | ||
|
|
cd226a7616 | ||
|
|
4243ce72f8 | ||
|
|
9065833e61 | ||
|
|
7cb7af6c8b | ||
|
|
90cbe7cec0 | ||
|
|
df33c1ad7b | ||
|
|
bbabea30dd | ||
|
|
8a61f8daa3 |
@@ -19,7 +19,7 @@ def main(ctx):
|
||||
linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv),
|
||||
linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True),
|
||||
linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv),
|
||||
#linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv),
|
||||
linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv),
|
||||
linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb",
|
||||
image="cppalliance/droneubuntu2004:1", buildtype="boost", buildscript="drone", environment={"GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "11", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True),
|
||||
# A set of jobs based on the earlier .travis.yml configuration:
|
||||
|
||||
@@ -26,7 +26,9 @@ 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 libs/!SELF!/example -j3
|
||||
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
|
||||
|
||||
) else if "%DRONE_JOB_BUILDTYPE%" == "standalone-windows" (
|
||||
|
||||
REM not used
|
||||
|
||||
174
.github/workflows/ci.yml
vendored
174
.github/workflows/ci.yml
vendored
@@ -17,6 +17,10 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: gcc-4.8
|
||||
cxxstd: "11"
|
||||
os: ubuntu-18.04
|
||||
install: g++-4.8
|
||||
- toolset: gcc-5
|
||||
cxxstd: "11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
@@ -28,10 +32,21 @@ jobs:
|
||||
- toolset: gcc-7
|
||||
cxxstd: "11,14,17"
|
||||
os: ubuntu-18.04
|
||||
- toolset: gcc-8
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-18.04
|
||||
install: g++-8
|
||||
- toolset: gcc-9
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: gcc-10
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: g++-10
|
||||
- toolset: gcc-11
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: g++-11
|
||||
- toolset: gcc-12
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
os: ubuntu-22.04
|
||||
@@ -199,3 +214,162 @@ 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
|
||||
|
||||
posix-cmake-install:
|
||||
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: Configure
|
||||
run: |
|
||||
cd ../boost-root
|
||||
mkdir __build__ && cd __build__
|
||||
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DCMAKE_INSTALL_PREFIX=~/.local ..
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target install
|
||||
|
||||
- name: Use the installed library
|
||||
run: |
|
||||
cd ../boost-root/libs/$LIBRARY/test/cmake_install_test && mkdir __build__ && cd __build__
|
||||
cmake -DCMAKE_INSTALL_PREFIX=~/.local ..
|
||||
cmake --build .
|
||||
ctest --output-on-failure --no-tests=error
|
||||
|
||||
posix-cmake-test:
|
||||
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: Configure
|
||||
run: |
|
||||
cd ../boost-root
|
||||
mkdir __build__ && cd __build__
|
||||
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DBUILD_TESTING=ON ..
|
||||
|
||||
- name: Build tests
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target tests
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
ctest --output-on-failure --no-tests=error
|
||||
|
||||
@@ -35,13 +35,13 @@ 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],
|
||||
which also have file-like names, but are in a different scope than the actual file system.
|
||||
|
||||
[note The main reason named pipes are part of this library, is because they need to be internally used for asynchronous communication on windows.]
|
||||
[note The main reason named pipes are part of this library, is because they need to be internally used for asynchrounous communication on windows.]
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
@@ -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;});
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ else if (pid == 0) //child process
|
||||
for (auto & s : seq)
|
||||
s.<methodname alt="boost::process::extend::handler::on_exec_error">on_exec_error</methodname>(*this);
|
||||
|
||||
<emphasis>unspecified();</emphasis>//here the error is sent to the father process internally
|
||||
<emphasis>unspecified();</emphasis>//here the error is send to the father process interally
|
||||
|
||||
<ulink url="http://en.cppreference.com/w/cpp/utility/program/exit">std::exit</ulink>(<ulink url="http://en.cppreference.com/w/c/program/EXIT_status">EXIT_FAILURE</ulink>);
|
||||
return <classname alt="boost::process::child">child</classname>(); //for C++ compliance
|
||||
@@ -39,7 +39,7 @@ else if (pid == 0) //child process
|
||||
|
||||
<classname alt="boost::process::child">child</classname> c(pid, exit_code);
|
||||
|
||||
<emphasis>unspecified();</emphasis>//here, we read the error from the child process
|
||||
<emphasis>unspecified();</emphasis>//here, we read the the error from the child process
|
||||
|
||||
if (<methodname alt="boost::process::extend::posix_executor::error">error</methodname>())
|
||||
for (auto & s : seq)
|
||||
@@ -48,7 +48,7 @@ else
|
||||
for (auto & s : seq)
|
||||
s.<methodname alt="boost::process::extend::handler::on_error">on_success</methodname>(*this);
|
||||
|
||||
//now we check again, because an on_success handler might've errored.
|
||||
//now we check again, because a on_success handler might've errored.
|
||||
if (<methodname alt="boost::process::extend::posix_executor::error">error</methodname>())
|
||||
{
|
||||
for (auto & s : seq)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,7 +9,7 @@ Additionally, environment can be lists separated by `:` or `;`; `environment::va
|
||||
`environment::value_view` can be used to iterate those.
|
||||
|
||||
Beyond that, the requirements on an environment are a low as possible;
|
||||
an environment is either a list of strings or a list of string-pairs. It is however recommended to use the environment types,
|
||||
an environment is either a list of strings or a list of string-pairs. It is however recommented to use the environment types,
|
||||
as to have the right value comparisons.
|
||||
|
||||
To note is the `find_executable` functions, which searches in an environment for an executable.
|
||||
@@ -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++");
|
||||
auto exe = find_executable("g++"), my_env);
|
||||
process proc(ctx, exe, {"main.cpp"}, process_environment(my_env));
|
||||
process pro2(ctx, exe, {"test.cpp"}, process_environment(my_env));
|
||||
```
|
||||
|
||||
@@ -27,7 +27,7 @@ For process v2, the interfaces is simple:
|
||||
extern asio::io_context ctx;
|
||||
process proc(ctx, "./test", {"--help"}, process_io{nullptr, {}, {}}, process_environment(my_env));
|
||||
|
||||
Every initializer addresses one logical component (e.g. stdio) instead of multiple ones accumulating.
|
||||
Every initializer adresses one logical compoent (e.g. stdio) instead of multiple ones accumulating.
|
||||
Furthermore, every process has a path and arguments, instead of a confusing mixture of cmd-style and
|
||||
exe-args that can be randomly spread out.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -21,7 +21,7 @@ A launcher is invoked through the call operator.
|
||||
```
|
||||
auto l = windows::as_user_launcher((HANDLE)0xDEADBEEF);
|
||||
asio::io_context ctx;
|
||||
boost::system::error_code ec;
|
||||
boost::system::eror_code ec;
|
||||
auto proc = l(ctx, ec, "C:\\User\\boost\\Downloads\\totally_not_a_virus.exe", {});
|
||||
```
|
||||
|
||||
@@ -38,7 +38,7 @@ Alternatively, the `vfork_launcher` can report errors directly back to the paren
|
||||
Thus some calls to the initializers occur after forking from the child process.
|
||||
|
||||
```
|
||||
struct custom_initializer
|
||||
struct custom_initalizer
|
||||
{
|
||||
// functions called from the parent process:
|
||||
|
||||
@@ -47,7 +47,7 @@ Thus some calls to the initializers occur after forking from the child process.
|
||||
template<typename Launcher>
|
||||
error_code on_setup(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line));
|
||||
|
||||
// called for every initializer if an error occurred during setup or process creation
|
||||
// called for every initializer if an error occured during setup or process creation
|
||||
template<typename Launcher>
|
||||
void on_error(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line),
|
||||
const error_code & ec);
|
||||
@@ -56,7 +56,7 @@ Thus some calls to the initializers occur after forking from the child process.
|
||||
template<typename Launcher>
|
||||
void on_success(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line));
|
||||
|
||||
// called for every initializer if an error occurred when forking, in addition to on_error.
|
||||
// called for every initializer if an error occured when forking, in addtion to on_error.
|
||||
template<typename Launcher>
|
||||
void on_fork_error(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line),
|
||||
const error_code & ec);
|
||||
@@ -94,7 +94,7 @@ The launcher will close all non-whitelisted file descriptors after `on_exec_setu
|
||||
|
||||
[section:windows Windows Launchers]
|
||||
|
||||
Windows launchers are pretty straight forward, they will call the following functions on the initializer if present.
|
||||
Windows launchers are pretty streight forward, they will call the following functions on the initializer if present.
|
||||
|
||||
```
|
||||
struct custom_initializer
|
||||
@@ -103,7 +103,7 @@ Windows launchers are pretty straight forward, they will call the following func
|
||||
template<typename Launcher>
|
||||
error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line);
|
||||
|
||||
// called for every initializer if an error occurred during setup or process creation
|
||||
// called for every initializer if an error occured during setup or process creation
|
||||
template<typename Launcher>
|
||||
void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
|
||||
const error_code & ec);
|
||||
|
||||
@@ -32,7 +32,7 @@ also returned from `.wait()`.
|
||||
```
|
||||
|
||||
The normal exit-code is what the subprocess returned from `main`;
|
||||
posix will however add additional information about the process.
|
||||
posix will however add addtional information about the process.
|
||||
This is called the `native_exit_code`.
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ The `.running()` function can be used to detect if the process is still active.
|
||||
|
||||
[section:signal Signalling the subprocess]
|
||||
|
||||
The parent process can signal the subprocess demanding certain actions.
|
||||
The parent process can signal the subprocess demaning certain actions.
|
||||
|
||||
`.terminate` will cause the subprocess to exit immediately (`SIGKILL` on posix).
|
||||
This is the only reliable & portable way to end a subprocess.
|
||||
@@ -76,7 +76,7 @@ interpret as a signal to shutdown.
|
||||
|
||||
[section:execute Execute functions]
|
||||
|
||||
Process v2 provides `execute` and `async_execute` functions that can be used for managed executions.
|
||||
Process v2 provides `execute` and `async_execute` functons that can be used for managed executions.
|
||||
|
||||
```
|
||||
assert(execute(process("/bin/ls", {}) == 0));
|
||||
@@ -90,7 +90,7 @@ The async version supports cancellation and will forward cancellation types as f
|
||||
|
||||
```
|
||||
asio::io_context ctx;
|
||||
asio::steady_timer timeout{ctx, std::chrono::seconds(10)};
|
||||
asio::steady_timer timout{ctx, std::chrono::seconds(10)};
|
||||
|
||||
asio::cancellation_signal sig;
|
||||
async_execute(process("/usr/bin/g++", {"hello_world.cpp"}),
|
||||
@@ -114,6 +114,7 @@ The async version supports cancellation and will forward cancellation types as f
|
||||
if (!ec)
|
||||
sig.emit(asio::cancellation_type::terminal);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
@@ -23,7 +23,7 @@ automatically connected and the other side will get assigned to the child proces
|
||||
proc.wait();
|
||||
```
|
||||
|
||||
readable pipes can be assigned to `out` an `err`, while writable_pipes can be assigned to `in`.
|
||||
readable pipes can be assigned to `out` an `err``, while writable_pipes can be assigned to `in`.
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ else
|
||||
for (auto & s : seq)
|
||||
s.<methodname alt="boost::process::extend::handler::on_error">on_success</methodname>(*this);
|
||||
|
||||
//now we check again, because an on_success handler might've errored.
|
||||
//now we check again, because a on_success handler might've errored.
|
||||
if (<methodname alt="boost::process::extend::windows_executor::error">error</methodname>())
|
||||
{
|
||||
for (auto & s : seq)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2022 Klemens Morgenstern
|
||||
// Copyright (c) 2022Klemens Morgernstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2022Klemens Morgenstern
|
||||
// Copyright (c) 2022Klemens Morgernstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/** \file boost/process/async.hpp
|
||||
|
||||
The header which provides the basic asynchronous features.
|
||||
The header which provides the basic asynchrounous features.
|
||||
It provides the on_exit property, which allows callbacks when the process exits.
|
||||
It also implements the necessary traits for passing an boost::asio::io_context,
|
||||
which is needed for asynchronous communication.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/**
|
||||
* \file boost/process/async_system.hpp
|
||||
*
|
||||
* Defines the asynchronous version of the system function.
|
||||
* Defines the asynchrounous version of the system function.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_PROCESS_ASYNC_SYSTEM_HPP
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
#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>
|
||||
@@ -73,33 +71,31 @@ inline std::error_code get_last_error() noexcept
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void throw_last_error(const std::string & msg, boost::source_location const & loc = boost::source_location())
|
||||
inline void throw_last_error(const std::string & msg)
|
||||
{
|
||||
boost::throw_exception(process_error(get_last_error(), msg), loc);
|
||||
throw process_error(get_last_error(), msg);
|
||||
}
|
||||
|
||||
inline void throw_last_error(const char * msg, boost::source_location const & loc = boost::source_location())
|
||||
inline void throw_last_error(const char * msg)
|
||||
{
|
||||
boost::throw_exception(process_error(get_last_error(), msg), loc);
|
||||
throw process_error(get_last_error(), msg);
|
||||
}
|
||||
|
||||
inline void throw_last_error(boost::source_location const & loc = boost::source_location())
|
||||
inline void throw_last_error()
|
||||
{
|
||||
boost::throw_exception(process_error(get_last_error()), loc);
|
||||
throw process_error(get_last_error());
|
||||
}
|
||||
|
||||
inline void throw_error(const std::error_code& ec,
|
||||
boost::source_location const & loc = boost::source_location())
|
||||
inline void throw_error(const std::error_code& ec)
|
||||
{
|
||||
if (ec)
|
||||
boost::throw_exception(process_error(ec), loc);
|
||||
throw process_error(ec);
|
||||
}
|
||||
|
||||
inline void throw_error(const std::error_code& ec, const char* msg,
|
||||
boost::source_location const & loc = boost::source_location())
|
||||
inline void throw_error(const std::error_code& ec, const char* msg)
|
||||
{
|
||||
if (ec)
|
||||
boost::throw_exception(process_error(ec, msg), loc);
|
||||
throw process_error(ec, msg);
|
||||
}
|
||||
|
||||
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 &)
|
||||
inline void on_success(Executor &exec)
|
||||
{
|
||||
auto pipe = this->pipe;
|
||||
boost::asio::async_read(*pipe, buf,
|
||||
|
||||
@@ -39,10 +39,10 @@ inline std::string build_cmd_shell(const std::string & exe, std::vector<std::str
|
||||
//the first one is put directly onto the output,
|
||||
//because then I don't have to copy the whole string
|
||||
arg.insert(arg.begin(), '"' );
|
||||
arg += '"'; //that is the post one.
|
||||
arg += '"'; //thats the post one.
|
||||
}
|
||||
|
||||
if (!st.empty())//first one does not need a preceding space
|
||||
if (!st.empty())//first one does not need a preceeding space
|
||||
st += ' ';
|
||||
|
||||
st += arg;
|
||||
@@ -112,10 +112,7 @@ struct exe_cmd_init<char> : boost::process::detail::api::handler_base_ext
|
||||
{
|
||||
if (exe.empty()) //cmd style
|
||||
{
|
||||
if (args.empty())
|
||||
exec.exe = "";
|
||||
else
|
||||
exec.exe = args.front().c_str();
|
||||
exec.exe = args.front().c_str();
|
||||
exec.cmd_style = true;
|
||||
}
|
||||
else
|
||||
@@ -158,15 +155,13 @@ private:
|
||||
|
||||
std::vector<char*> exe_cmd_init<char>::make_cmd()
|
||||
{
|
||||
// any string must be writable.
|
||||
static char empty_string[1] = "";
|
||||
std::vector<char*> vec;
|
||||
if (!exe.empty())
|
||||
vec.push_back(exe.empty() ? empty_string : &exe.front());
|
||||
vec.push_back(&exe.front());
|
||||
|
||||
if (!args.empty()) {
|
||||
for (auto & v : args)
|
||||
vec.push_back(v.empty() ? empty_string : &v.front());
|
||||
vec.push_back(&v.front());
|
||||
}
|
||||
|
||||
vec.push_back(nullptr);
|
||||
|
||||
@@ -77,9 +77,12 @@ 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)
|
||||
{
|
||||
ssize_t write_len;
|
||||
int_type write_len;
|
||||
while ((write_len = ::write(_sink, data, count * sizeof(char_type))) == -1)
|
||||
{
|
||||
//Try again if interrupted
|
||||
@@ -87,19 +90,19 @@ public:
|
||||
if (err != EINTR)
|
||||
::boost::process::detail::throw_last_error();
|
||||
}
|
||||
return static_cast<int_type>(write_len);
|
||||
return write_len;
|
||||
}
|
||||
int_type read(char_type * data, int_type count)
|
||||
{
|
||||
ssize_t read_len;
|
||||
while ((read_len = ::read(_source, data, count * sizeof(char_type))) == -1)
|
||||
int_type read_len;
|
||||
while ((read_len = static_cast<int_type>(::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 static_cast<int_type>(read_len);
|
||||
return read_len;
|
||||
}
|
||||
|
||||
bool is_open() const
|
||||
|
||||
@@ -444,7 +444,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
|
||||
}
|
||||
if (_ec)
|
||||
{
|
||||
//if an error occurred we need to reap the child process
|
||||
//if an error occured we need to reap the child process
|
||||
::waitpid(this->pid, nullptr, WNOHANG);
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -80,7 +80,7 @@ struct io_context_ref : handler_base_ext
|
||||
void on_success(Executor& exec)
|
||||
{
|
||||
ios.notify_fork(boost::asio::io_context::fork_parent);
|
||||
//must be on the heap, so I can move it into the lambda.
|
||||
//must be on the heap so I can move it into the lambda.
|
||||
auto asyncs = boost::fusion::filter_if<
|
||||
is_async_handler<
|
||||
typename std::remove_reference< boost::mpl::_ > ::type
|
||||
|
||||
@@ -34,7 +34,7 @@ inline bool is_running(const child_handle &p, int & exit_code, std::error_code &
|
||||
|
||||
if (ret == -1)
|
||||
{
|
||||
if (errno != ECHILD) //because it no child is running, then this one isn't either, obviously.
|
||||
if (errno != ECHILD) //because it no child is running, than this one isn't either, obviously.
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,11 +7,8 @@
|
||||
#ifndef BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
|
||||
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/consign.hpp>
|
||||
#include <boost/asio/append.hpp>
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
@@ -29,43 +26,6 @@ class sigchld_service : public boost::asio::detail::service_base<sigchld_service
|
||||
|
||||
std::list<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;
|
||||
inline void _handle_signal(const boost::system::error_code & ec);
|
||||
|
||||
struct initiate_async_wait_op
|
||||
{
|
||||
sigchld_service * self;
|
||||
template<typename Initiation>
|
||||
void operator()(Initiation && init, ::pid_t pid)
|
||||
{
|
||||
// check if the child actually is running first
|
||||
int status;
|
||||
auto pid_res = ::waitpid(pid, &status, WNOHANG);
|
||||
if (pid_res < 0)
|
||||
{
|
||||
auto ec = get_last_error();
|
||||
boost::asio::post(
|
||||
self->_strand,
|
||||
asio::append(std::forward<Initiation>(init), pid_res, ec));
|
||||
}
|
||||
else if ((pid_res == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
boost::asio::post(
|
||||
self->_strand,
|
||||
boost::asio::append(std::forward<Initiation>(init), status, std::error_code{}));
|
||||
else //still running
|
||||
{
|
||||
sigchld_service * self_ = self;
|
||||
if (self->_receivers.empty())
|
||||
self->_signal_set.async_wait(
|
||||
boost::asio::bind_executor(
|
||||
self->_strand,
|
||||
[self_](const boost::system::error_code &ec, int)
|
||||
{
|
||||
self_->_handle_signal(ec);
|
||||
}));
|
||||
self->_receivers.emplace_back(pid, init);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
sigchld_service(boost::asio::io_context & io_context)
|
||||
: boost::asio::detail::service_base<sigchld_service>(io_context)
|
||||
@@ -77,10 +37,47 @@ public:
|
||||
void (int, std::error_code))
|
||||
async_wait(::pid_t pid, SignalHandler && handler)
|
||||
{
|
||||
return boost::asio::async_initiate<
|
||||
SignalHandler,
|
||||
void(int, std::error_code)>(
|
||||
initiate_async_wait_op{this}, handler, pid);
|
||||
boost::asio::async_completion<
|
||||
SignalHandler, void(boost::system::error_code)> init{handler};
|
||||
|
||||
auto & h = init.completion_handler;
|
||||
boost::asio::dispatch(
|
||||
_strand,
|
||||
[this, pid, h]
|
||||
{
|
||||
//check if the child actually is running first
|
||||
int status;
|
||||
auto pid_res = ::waitpid(pid, &status, WNOHANG);
|
||||
if (pid_res < 0)
|
||||
{
|
||||
auto ec = get_last_error();
|
||||
boost::asio::post(
|
||||
_strand,
|
||||
[pid_res, ec, h]
|
||||
{
|
||||
h(pid_res, ec);
|
||||
});
|
||||
}
|
||||
else if ((pid_res == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
boost::asio::post(
|
||||
_strand,
|
||||
[status, h]
|
||||
{
|
||||
h(status, {}); //successfully exited already
|
||||
});
|
||||
else //still running
|
||||
{
|
||||
if (_receivers.empty())
|
||||
_signal_set.async_wait(
|
||||
[this](const boost::system::error_code &ec, int)
|
||||
{
|
||||
boost::asio::dispatch(_strand, [this, ec]{this->_handle_signal(ec);});
|
||||
});
|
||||
_receivers.emplace_back(pid, h);
|
||||
}
|
||||
});
|
||||
|
||||
return init.result.get();
|
||||
}
|
||||
void shutdown() override
|
||||
{
|
||||
|
||||
@@ -61,12 +61,8 @@ 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
|
||||
if (!st.empty())//first one does not need a preceeding space
|
||||
st += ' ';
|
||||
|
||||
st += arg;
|
||||
@@ -109,12 +105,8 @@ 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
|
||||
if (!st.empty())//first one does not need a preceeding space
|
||||
st += L' ';
|
||||
|
||||
st += arg;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -81,7 +81,7 @@ struct child_handle
|
||||
{
|
||||
::boost::winapi::BOOL_ value;
|
||||
if (!::boost::winapi::IsProcessInJob(proc_info.hProcess, nullptr, &value))
|
||||
throw_last_error("IsProcessInJob Failed");
|
||||
throw_last_error("IsProcessinJob Failed");
|
||||
return value!=0;
|
||||
}
|
||||
bool in_group(std::error_code &ec) const noexcept
|
||||
|
||||
@@ -73,8 +73,8 @@ inline auto native_environment_impl<Char>::get(const pointer_type id) -> string_
|
||||
|
||||
if (size == sizeof(buf)) //the return size gives the size without the null, so I know this went wrong
|
||||
{
|
||||
/* limit defined here https://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx
|
||||
* but I used 32768, so it is a multiple of 4096.
|
||||
/*limit defined here https://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx
|
||||
* but I used 32768 so it is a multiple of 4096.
|
||||
*/
|
||||
constexpr static std::size_t max_size = 32768;
|
||||
//Handle variables longer then buf.
|
||||
@@ -232,7 +232,7 @@ basic_environment_impl<Char>::basic_environment_impl(const native_environment_im
|
||||
template<typename Char>
|
||||
inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type
|
||||
{
|
||||
if (id.size() >= _data.size()) //ok, so it is impossible id is in there.
|
||||
if (id.size() >= _data.size()) //ok, so it's impossible id is in there.
|
||||
return string_type(_data.data());
|
||||
|
||||
if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == equal_sign<Char>()))
|
||||
@@ -273,7 +273,7 @@ template<typename Char>
|
||||
inline void basic_environment_impl<Char>::reset(const string_type &id)
|
||||
{
|
||||
//ok, we need to check the size of data first
|
||||
if (id.size() >= _data.size()) //ok, so it is impossible id is in there.
|
||||
if (id.size() >= _data.size()) //ok, so it's impossible id is in there.
|
||||
return;
|
||||
|
||||
//check if it's the first one, spares us the search.
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <boost/process/detail/windows/handler.hpp>
|
||||
#include <boost/winapi/get_current_process_id.hpp>
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/winapi/handle_info.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail {
|
||||
|
||||
@@ -164,7 +163,7 @@ struct limit_handles_ : handler_base_ext
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
void on_success(Executor & exec) const
|
||||
void on_sucess(Executor & exec) const
|
||||
{
|
||||
for (auto handle : handles_with_inherit_flag)
|
||||
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, ::boost::winapi::HANDLE_FLAG_INHERIT_);
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#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
|
||||
{
|
||||
|
||||
@@ -437,7 +437,7 @@ for both `id` and `value`.
|
||||
|
||||
\subsubsection env_reset Reset variables
|
||||
|
||||
Resetting single variables can be done in the following way:
|
||||
Reseting signle variables can be done in the following way:
|
||||
|
||||
\code{.cpp}
|
||||
env[id] = boost::none;
|
||||
|
||||
@@ -263,7 +263,7 @@ public:
|
||||
auto st1 = key + ::boost::process::detail::equal_sign<Char>();
|
||||
while (*p != nullptr)
|
||||
{
|
||||
const std::size_t len = std::char_traits<Char>::length(*p);
|
||||
const int 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 std::size_t len = std::char_traits<Char>::length(*p);
|
||||
const int len = std::char_traits<Char>::length(*p);
|
||||
if ((std::distance(st1.begin(), st1.end()) < len)
|
||||
&& std::equal(st1.begin(), st1.end(), *p))
|
||||
break;
|
||||
@@ -292,9 +292,8 @@ public:
|
||||
auto st1 = st + ::boost::process::detail::equal_sign<Char>();
|
||||
while (*p != nullptr)
|
||||
{
|
||||
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))
|
||||
const int len = std::char_traits<Char>::length(*p);
|
||||
if ((std::distance(st1.begin(), st1.end()) < len)
|
||||
&& std::equal(st1.begin(), st1.end(), *p))
|
||||
return 1u;
|
||||
p++;
|
||||
|
||||
@@ -70,7 +70,7 @@ using ::boost::process::detail::get_used_handles;
|
||||
|
||||
///This handler is invoked before the process in launched, to setup parameters. The required signature is `void(Exec &)`, where `Exec` is a template parameter.
|
||||
constexpr boost::process::detail::make_handler_t<boost::process::detail::on_setup_> on_setup;
|
||||
///This handler is invoked if an error occurred. The required signature is `void(auto & exec, const std::error_code&)`, where `Exec` is a template parameter.
|
||||
///This handler is invoked if an error occured. The required signature is `void(auto & exec, const std::error_code&)`, where `Exec` is a template parameter.
|
||||
constexpr boost::process::detail::make_handler_t<boost::process::detail::on_error_> on_error;
|
||||
///This handler is invoked if launching the process has succeeded. The required signature is `void(auto & exec)`, where `Exec` is a template parameter.
|
||||
constexpr boost::process::detail::make_handler_t<boost::process::detail::on_success_> on_success;
|
||||
|
||||
@@ -82,7 +82,7 @@ using limit_handles_ = ::boost::process::detail::api::limit_handles_;
|
||||
}
|
||||
|
||||
/**
|
||||
* The limit_handles property sets all properties to be inherited only explicitly. It closes all unused file-descriptors on posix after the fork and
|
||||
* The limit_handles property sets all properties to be inherited only expcitly. It closes all unused file-descriptors on posix after the fork and
|
||||
* removes the inherit flags on windows.
|
||||
*
|
||||
* \note This is executed after the fork on posix.
|
||||
|
||||
@@ -123,14 +123,8 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
///Destructor -> writes the frest of the data
|
||||
~basic_pipebuf()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (basic_pipebuf::is_open())
|
||||
basic_pipebuf::overflow(Traits::eof());
|
||||
}
|
||||
catch (process_error & )
|
||||
{
|
||||
}
|
||||
if (basic_pipebuf::is_open())
|
||||
basic_pipebuf::overflow(Traits::eof());
|
||||
}
|
||||
|
||||
///Move construct from a pipe.
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace boost {
|
||||
|
||||
namespace boost { namespace process {
|
||||
|
||||
///Namespace containing the posix extensions.
|
||||
///Namespace containing the posix exensions.
|
||||
namespace posix {
|
||||
|
||||
/** This property lets you modify file-descriptors other than the standard ones (0,1,2).
|
||||
|
||||
@@ -128,7 +128,7 @@ inline int get_cont_octet_out_count_impl<4>(wchar_t word) {
|
||||
|
||||
// Note that the following code will generate warnings on some platforms
|
||||
// where wchar_t is defined as UCS2. The warnings are superfluous as the
|
||||
// specialization is never instantiated with such compilers, but this
|
||||
// specialization is never instantitiated with such compilers, but this
|
||||
// can cause problems if warnings are being treated as errors, so we guard
|
||||
// against that. Including <boost/detail/utf8_codecvt_facet.hpp> as we do
|
||||
// should be enough to get WCHAR_MAX defined.
|
||||
|
||||
@@ -85,7 +85,7 @@ struct basic_process_handle_win
|
||||
|
||||
template<typename Executor1>
|
||||
basic_process_handle_win(basic_process_handle_win<Executor1> && handle)
|
||||
: pid_(handle.pid_), handle_(std::move(handle.handle_))
|
||||
: pid_(handle.pid_), handle_(handle.handle_.get_executor())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#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>
|
||||
@@ -489,7 +488,7 @@ struct key
|
||||
using string_type = std::basic_string<char_type, traits_type>;
|
||||
using string_view_type = basic_string_view<char_type, traits_type>;
|
||||
|
||||
key() {}
|
||||
key() noexcept = default;
|
||||
key( const key& p ) = default;
|
||||
key( key&& p ) noexcept = default;
|
||||
key( const string_type& source ) : value_(source) {}
|
||||
@@ -502,8 +501,8 @@ struct key
|
||||
|
||||
template< class Source >
|
||||
key( const Source& source,
|
||||
decltype(std::declval<Source>().data()) = nullptr,
|
||||
decltype(std::declval<Source>().size()) = 0u)
|
||||
decltype(source.data()) = nullptr,
|
||||
decltype(source.size()) = 0u)
|
||||
: value_(
|
||||
BOOST_PROCESS_V2_NAMESPACE::detail::conv_string<char_type, traits_type>(
|
||||
source.data(), source.size()))
|
||||
@@ -525,11 +524,7 @@ struct key
|
||||
~key() = default;
|
||||
|
||||
key& operator=( const key& p ) = default;
|
||||
key& operator=( key&& p )
|
||||
{
|
||||
value_ = std::move(p.value_);
|
||||
return *this;
|
||||
}
|
||||
key& operator=( key&& p ) noexcept(std::is_nothrow_move_constructible<string_type>::value) = default;
|
||||
key& operator=( string_type&& source )
|
||||
{
|
||||
value_ = std::move(source);
|
||||
@@ -713,7 +708,7 @@ struct value
|
||||
using string_type = std::basic_string<char_type, traits_type>;
|
||||
using string_view_type = basic_cstring_ref<char_type, traits_type>;
|
||||
|
||||
value() {}
|
||||
value() noexcept = default;
|
||||
value( const value& p ) = default;
|
||||
|
||||
value( const string_type& source ) : value_(source) {}
|
||||
@@ -725,8 +720,8 @@ struct value
|
||||
|
||||
template< class Source >
|
||||
value( const Source& source,
|
||||
decltype(std::declval<Source>().data()) = nullptr,
|
||||
decltype(std::declval<Source>().size()) = 0u)
|
||||
decltype(source.data()) = nullptr,
|
||||
decltype(source.size()) = 0u)
|
||||
: value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string<char_type, traits_type>(
|
||||
source.data(), source.size()))
|
||||
{
|
||||
@@ -747,11 +742,7 @@ struct value
|
||||
~value() = default;
|
||||
|
||||
value& operator=( const value& p ) = default;
|
||||
value& operator=( value&& p )
|
||||
{
|
||||
value_ = std::move(p.value_);
|
||||
return *this;
|
||||
}
|
||||
value& operator=( value&& p ) noexcept(std::is_nothrow_move_constructible<string_type>::value) = default;
|
||||
value& operator=( string_type&& source )
|
||||
{
|
||||
value_ = std::move(source);
|
||||
@@ -944,7 +935,7 @@ struct key_value_pair
|
||||
using string_type = std::basic_string<char_type>;
|
||||
using string_view_type = basic_cstring_ref<char_type>;
|
||||
|
||||
key_value_pair() {}
|
||||
key_value_pair() noexcept = default;
|
||||
key_value_pair( const key_value_pair& p ) = default;
|
||||
key_value_pair( key_value_pair&& p ) noexcept = default;
|
||||
key_value_pair(key_view key, value_view value) : value_(key.basic_string<char_type, traits_type>() + equality_sign +
|
||||
@@ -975,8 +966,8 @@ struct key_value_pair
|
||||
|
||||
template< class Source >
|
||||
key_value_pair( const Source& source,
|
||||
decltype(std::declval<Source>().data()) = nullptr,
|
||||
decltype(std::declval<Source>().size()) = 0u)
|
||||
decltype(source.data()) = nullptr,
|
||||
decltype(source.size()) = 0u)
|
||||
: value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string<char_type, traits_type>(
|
||||
source.data(), source.size()))
|
||||
{
|
||||
@@ -1008,11 +999,7 @@ struct key_value_pair
|
||||
~key_value_pair() = default;
|
||||
|
||||
key_value_pair& operator=( const key_value_pair& p ) = default;
|
||||
key_value_pair& operator=( key_value_pair&& p )
|
||||
{
|
||||
value_ = std::move(p.value_);
|
||||
return *this;
|
||||
}
|
||||
key_value_pair& operator=( key_value_pair&& p ) noexcept(std::is_nothrow_move_constructible<string_type>::value) = default;
|
||||
key_value_pair& operator=( string_type&& source )
|
||||
{
|
||||
value_ = std::move(source);
|
||||
@@ -1744,8 +1731,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)} {}
|
||||
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"")} {}
|
||||
|
||||
template<typename Args>
|
||||
process_environment(Args && args) : unicode_env{build_env(std::forward<Args>(args))}
|
||||
@@ -1757,7 +1744,6 @@ 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 &);
|
||||
|
||||
@@ -1806,7 +1792,6 @@ struct process_environment
|
||||
}
|
||||
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
error_code on_setup(posix::default_launcher & launcher,
|
||||
const filesystem::path &, const char * const *);
|
||||
|
||||
@@ -1888,7 +1873,6 @@ 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
|
||||
|
||||
@@ -22,7 +22,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
* @tparam Executor The asio executor of the process handle
|
||||
* @param proc The process to be run.
|
||||
* @return int The exit code of the process
|
||||
* @exception system_error An error that might have occurred during the wait.
|
||||
* @exception system_error An error that might have occured during the wait.
|
||||
*/
|
||||
template<typename Executor>
|
||||
inline int execute(basic_process<Executor> proc)
|
||||
@@ -66,7 +66,7 @@ struct execute_op
|
||||
template<typename Self>
|
||||
void operator()(Self && self)
|
||||
{
|
||||
self.reset_cancellation_state(BOOST_PROCESS_V2_ASIO_NAMESPACE::enable_total_cancellation());
|
||||
self.reset_cancellation_state();
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_slot s = self.get_cancellation_state().slot();
|
||||
if (s.is_connected())
|
||||
s.emplace<cancel>(proc.get());
|
||||
@@ -92,13 +92,13 @@ struct execute_op
|
||||
/** This function asynchronously for a process to complete.
|
||||
*
|
||||
* Cancelling the execution will signal the child process to exit
|
||||
* with the following interpretations:
|
||||
* with the following intepretations:
|
||||
*
|
||||
* - cancellation_type::total -> interrupt
|
||||
* - cancellation_type::partial -> request_exit
|
||||
* - cancellation_type::terminal -> terminate
|
||||
*
|
||||
* It is to note that `async_execute` will us the lowest selected cancellation
|
||||
* It is to note that `async_execute` will us the lowest seelected cancellation
|
||||
* type. A subprocess might ignore anything not terminal.
|
||||
*/
|
||||
template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor,
|
||||
|
||||
@@ -94,19 +94,12 @@ inline int evaluate_exit_code(int code)
|
||||
|
||||
#endif
|
||||
|
||||
/// @{
|
||||
/** Helper to subsume an exit-code into an error_code if there's no actual error isn't set.
|
||||
|
||||
/** Convert the exit-code in a completion into an error if the actual error isn't set.
|
||||
* @code {.cpp}
|
||||
* process proc{ctx, "exit", {"1"}};
|
||||
*
|
||||
* proc.async_wait(
|
||||
* asio::deferred(
|
||||
* [&proc](error_code ec, int)
|
||||
* {
|
||||
* return asio::deferred.values(
|
||||
* check_exit_code(ec, proc.native_exit_code())
|
||||
* );
|
||||
*
|
||||
* proc.async_wait(code_as_error(
|
||||
* [](error_code ec)
|
||||
* {
|
||||
* assert(ec.value() == 10);
|
||||
@@ -114,19 +107,144 @@ inline int evaluate_exit_code(int code)
|
||||
* }));
|
||||
*
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
inline error_code check_exit_code(
|
||||
error_code &ec, native_exit_code_type native_code,
|
||||
const error_category & category = error::get_exit_code_category())
|
||||
*/
|
||||
template<typename CompletionToken>
|
||||
struct code_as_error_t
|
||||
{
|
||||
if (!ec)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, native_code, category);
|
||||
return ec;
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
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,11 +20,6 @@ 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,11 +15,6 @@ 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);
|
||||
@@ -47,6 +42,12 @@ 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,11 +109,6 @@ 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);
|
||||
@@ -141,6 +136,10 @@ 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,11 +16,6 @@ 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);
|
||||
@@ -50,6 +45,11 @@ 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
|
||||
|
||||
@@ -92,12 +92,13 @@ struct make_cmd_shell_
|
||||
str_lengths += (std::strlen(*c) + 1);
|
||||
}
|
||||
// yes, not the greatest solution.
|
||||
std::string buffer;
|
||||
res.buffer_.resize(str_lengths);
|
||||
|
||||
res.argv_ = new char*[res.argc_ + 1];
|
||||
res.free_argv_ = +[](int argc, char ** argv) {delete[] argv;};
|
||||
res.argv_[res.argc_] = nullptr;
|
||||
auto p = &*res.buffer_.begin();
|
||||
auto p = &buffer[sizeof(int) * (res.argc_) + 1];
|
||||
|
||||
for (int i = 0; i < res.argc_; i++)
|
||||
{
|
||||
@@ -146,10 +147,7 @@ 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);
|
||||
return shell{};
|
||||
}
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
else
|
||||
return cmd(proc.get(), ec);
|
||||
|
||||
@@ -307,8 +305,9 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
return {};
|
||||
}
|
||||
|
||||
auto res = make_cmd_shell_::clone(cmd);
|
||||
procstat_freeargv(proc_stat.get());
|
||||
procstat_freeargv(proc_stat);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,15 +112,8 @@ 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
|
||||
);
|
||||
#elif defined(__sun)
|
||||
return fileystem::canonical(
|
||||
filesystem::path("/proc") / std::to_string(pid) / "path/cwd", ec
|
||||
);
|
||||
#endif
|
||||
filesystem::path("/proc") / std::to_string(pid) / "cwd", ec);
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__)
|
||||
|
||||
@@ -342,7 +342,7 @@ env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
auto eno = reinterpret_cast<char**>(out);
|
||||
auto eeo = eno;
|
||||
auto str = out + (n * sizeof(char*)) + sizeof(char*);
|
||||
auto str = out + (n * sizeof(char*)) + sizeof(char*);
|
||||
e = env;
|
||||
while (*e != nullptr)
|
||||
{
|
||||
@@ -351,11 +351,11 @@ env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
*eno = str;
|
||||
str += len;
|
||||
eno ++;
|
||||
e++;
|
||||
}
|
||||
*eno = nullptr;
|
||||
|
||||
ev.handle_.reset(eeo);
|
||||
|
||||
}
|
||||
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", ec
|
||||
filesystem::path("/proc") / std::to_string(pid) / "path/a.out"
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ struct exit_code_category final : public error_category
|
||||
}
|
||||
std::string message(int status) const
|
||||
{
|
||||
switch (evaluate_exit_code(status))
|
||||
switch (status)
|
||||
{
|
||||
case v2::detail::still_active:
|
||||
return "still-active";
|
||||
|
||||
@@ -147,14 +147,15 @@ std::vector<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
|
||||
std::vector<pid_type> all_pids(boost::system::error_code & ec)
|
||||
{
|
||||
std::vector<pid_type> vec;
|
||||
vec.resize(proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0) / sizeof(pid_type));
|
||||
const auto sz = proc_listpids(PROC_ALL_PIDS, 0, &vec[0], sizeof(pid_type) * vec.size());
|
||||
if (sz < 0)
|
||||
vec.reserve(proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0));
|
||||
if (proc_listpids(PROC_ALL_PIDS, 0, &vec[0], sizeof(pid_type) * vec.size()))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
return {};
|
||||
}
|
||||
vec.resize(sz);
|
||||
auto itr = std::partition(vec.begin(), vec.end(), [](pid_type pt) {return pt != 0;});
|
||||
vec.erase(itr, vec.end());
|
||||
std::reverse(vec.begin(), vec.end());
|
||||
return vec;
|
||||
}
|
||||
|
||||
@@ -175,14 +176,15 @@ pid_type parent_pid(pid_type pid, boost::system::error_code & ec)
|
||||
std::vector<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
std::vector<pid_type> vec;
|
||||
vec.resize(proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, nullptr, 0) / sizeof(pid_type));
|
||||
const auto sz = proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, &vec[0], sizeof(pid_type) * vec.size());
|
||||
if (sz < 0)
|
||||
vec.reserve(proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, nullptr, 0));
|
||||
if (proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, &vec[0], sizeof(pid_type) * vec.size()))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
return {};
|
||||
}
|
||||
vec.resize(sz);
|
||||
auto itr = std::partition(vec.begin(), vec.end(), [](pid_type pt) {return pt != 0;});
|
||||
vec.erase(itr, vec.end());
|
||||
std::reverse(vec.begin(), vec.end());
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ void shell::parse_()
|
||||
|
||||
shell::~shell()
|
||||
{
|
||||
if (argv_ != nullptr && free_argv_ != nullptr)
|
||||
if (argv_ != nullptr && free_argv_)
|
||||
free_argv_(argc_, argv_);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,42 @@ 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(
|
||||
|
||||
@@ -61,7 +61,7 @@ struct bind_fd
|
||||
*/
|
||||
bind_fd(int target, FILE * f) : bind_fd(target, fileno(f)) {}
|
||||
|
||||
/// Inherit a file descriptor with as a different value.
|
||||
/// Inherit a file descriptor with as a differnet value.
|
||||
/**
|
||||
* This will pass 24 as 42 to the child process:
|
||||
* @code
|
||||
@@ -79,7 +79,7 @@ struct bind_fd
|
||||
*/
|
||||
bind_fd(int target, std::nullptr_t) : bind_fd(target, filesystem::path("/dev/null")) {}
|
||||
|
||||
/// Inherit a newly opened-file as a set descriptor.
|
||||
/// Inherit a newly openedfile as a set descriptor.
|
||||
/**
|
||||
* This will pass 24 as 42 to the child process:
|
||||
* @code
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#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)
|
||||
@@ -314,7 +313,7 @@ struct default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -345,7 +344,7 @@ struct default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// boost/process/v2/posix/detail/close_handles.hpp
|
||||
// boost/process/v2/poxix/detail/close_handles.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
|
||||
|
||||
@@ -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)
|
||||
v2::detail::throw_error(ec, "fork_and_forget_launcher");
|
||||
asio::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)
|
||||
v2::detail::throw_error(ec, "fork_and_forget_launcher");
|
||||
asio::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)
|
||||
v2::detail::throw_error(ec, "pdfork_launcher");
|
||||
asio::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)
|
||||
v2::detail::throw_error(ec, "pdfork_launcher");
|
||||
asio::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)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
asio::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)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -101,6 +101,7 @@ 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);
|
||||
@@ -109,7 +110,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,7 +93,17 @@ 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(
|
||||
@@ -255,12 +265,12 @@ struct basic_process
|
||||
{
|
||||
error_code ec;
|
||||
if (running(ec))
|
||||
process_handle_.wait(exit_status_, ec);
|
||||
wait(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "wait failed");
|
||||
return exit_code();
|
||||
}
|
||||
/// Waits for the process to exit, store the exit code internally and return it.
|
||||
/// Waits for the process to exit, store the exit code internall and return it.
|
||||
int wait(error_code & ec)
|
||||
{
|
||||
if (running(ec))
|
||||
@@ -277,9 +287,8 @@ 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_);
|
||||
@@ -325,7 +334,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 native exit-code in the completion handler.
|
||||
/// Asynchronously wait for the process to exit and deliver the portable 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))
|
||||
|
||||
@@ -27,8 +27,8 @@ static const error_category& shell_category = get_shell_category();
|
||||
* In v1, this was possible directly when starting a process,
|
||||
* but has been removed based on the security risks associated with this.
|
||||
*
|
||||
* By making the shell parsing explicitly, it encourages
|
||||
* a user to run a sanity check on the executable before launching it.
|
||||
* By making the shell parsing explicity, it is encouraged
|
||||
* that a user runs a sanity check on the executable before launching it.
|
||||
*
|
||||
* @par Example
|
||||
* @code {.cpp}
|
||||
|
||||
@@ -103,15 +103,9 @@ struct process_io_binding
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & pipe)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
@@ -119,19 +113,14 @@ struct process_io_binding
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
h = std::unique_ptr<void, handle_closer>{p[1], true};
|
||||
pipe.assign(p[0]);
|
||||
readable_pipe.assign(p[0]);
|
||||
}
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & pipe)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
@@ -139,7 +128,7 @@ struct process_io_binding
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
h = std::unique_ptr<void, handle_closer>{p[0], true};
|
||||
pipe.assign(p[1]);
|
||||
writable_pipe.assign(p[1]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -181,19 +170,13 @@ struct process_io_binding
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & readable_pipe)
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & readable_pipe,
|
||||
typename std::enable_if<Target != STDIN_FILENO, Executor*>::type = 0)
|
||||
{
|
||||
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)
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
return ;
|
||||
fd = p[1];
|
||||
if (::fcntl(p[0], F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
@@ -206,20 +189,13 @@ struct process_io_binding
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & writable_pipe)
|
||||
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & writable_pipe,
|
||||
typename std::enable_if<Target == STDIN_FILENO, Executor*>::type = 0)
|
||||
{
|
||||
|
||||
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)
|
||||
detail::throw_error(ec, "create_pipe");
|
||||
|
||||
return ;
|
||||
fd = p[0];
|
||||
if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
@@ -289,7 +265,7 @@ typedef process_io_binding<STDERR_FILENO> process_error_binding;
|
||||
* - `FILE*` any open file, including `stdin`, `stdout` and `stderr`
|
||||
* - a filesystem::path, which will open a readable or writable depending on the direction of the stream
|
||||
* - `native_handle` any native file handle (`HANDLE` on windows) or file descriptor (`int` on posix)
|
||||
* - any io-object with a .native_handle() function that is compatible with the above. E.g. a asio::ip::tcp::socket
|
||||
* - any io-object with a .native_handle() function that is comptaiblie with the above. E.g. a asio::ip::tcp::socket
|
||||
* - an asio::basic_writeable_pipe for stdin or asio::basic_readable_pipe for stderr/stdout.
|
||||
*
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
v2::detail::throw_error(ec, "as_user_launcher");
|
||||
asio::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)
|
||||
detail::throw_error(ec, "as_user_launcher");
|
||||
asio::detail::throw_error(ec, "as_user_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
@@ -31,19 +31,14 @@ struct process_creation_flags
|
||||
const filesystem::path &,
|
||||
const std::wstring &) const
|
||||
{
|
||||
launcher.creation_flags |= Flags;
|
||||
launcher.startup_info.StartupInfo.dwFlags |= Flags;
|
||||
return error_code {};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// A flag to create a new process group. Necessary to allow interrupts for the subprocess.
|
||||
/// A flag to create a new process group. Necessary to allow interupts for the subproces.
|
||||
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,7 +14,6 @@
|
||||
#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>
|
||||
|
||||
@@ -245,7 +244,7 @@ struct default_launcher
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -276,7 +275,7 @@ struct default_launcher
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
detail::throw_error(ec, "default_launcher");
|
||||
asio::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
@@ -312,6 +311,7 @@ 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, password, domain;
|
||||
std::wstring username, domain, password;
|
||||
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)
|
||||
v2::detail::throw_error(ec, "with_logon_launcher");
|
||||
asio::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)
|
||||
v2::detail::throw_error(ec, "with_token_launcher");
|
||||
asio::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)
|
||||
detail::throw_error(ec, "with_token_launcher");
|
||||
asio::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::scope_exit Boost::thread Boost::unit_test_framework)
|
||||
target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem 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()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
// Created by kleme on 26.02.2018.
|
||||
//
|
||||
|
||||
#define BOOST_ASIO_NO_DEPRECATED 1
|
||||
#include <boost/process.hpp>
|
||||
|
||||
@@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(multithreaded_async_pipe)
|
||||
asio::io_context ioc;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
for (auto i = 0u; i < std::thread::hardware_concurrency(); i++)
|
||||
for (int i = 0; i < std::thread::hardware_concurrency(); i++)
|
||||
{
|
||||
threads.emplace_back([&ioc]
|
||||
{
|
||||
@@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(move_pipe)
|
||||
}
|
||||
/*
|
||||
{
|
||||
//copy a closed pipe
|
||||
//copy an a closed pipe
|
||||
BOOST_TEST_CHECKPOINT("Copy assign");
|
||||
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
@@ -131,7 +131,7 @@ BOOST_AUTO_TEST_CASE(move_pipe)
|
||||
}
|
||||
|
||||
{
|
||||
//copy a closed pipe
|
||||
//copy an a closed pipe
|
||||
BOOST_TEST_CHECKPOINT("Copy assign");
|
||||
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace bp = boost::process;
|
||||
namespace fs = boost::process::filesystem;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(explicit_)
|
||||
BOOST_AUTO_TEST_CASE(excplicit)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
@@ -83,24 +83,3 @@ BOOST_AUTO_TEST_CASE(implicit)
|
||||
BOOST_TEST_MESSAGE(ec.message());
|
||||
BOOST_CHECK_EQUAL(ret, 21);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(empty_cmd)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
fs::path pth = master_test_suite().argv[1];
|
||||
auto env = boost::this_process::environment();
|
||||
|
||||
auto itr = std::find_if(env.begin(), env.end(),
|
||||
[](const bp::native_environment::entry_type & e){return boost::to_upper_copy(e.get_name()) == "PATH";});
|
||||
|
||||
BOOST_REQUIRE(itr != env.end());
|
||||
|
||||
(*itr) += fs::canonical(fs::absolute(pth.parent_path())).string();
|
||||
BOOST_REQUIRE(itr != env.end());
|
||||
|
||||
bp::system("sparring_partner \"\" ", ec);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(wait_group_test, *boost::unit_test::timeout(5))
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_REQUIRE(c2.in_group(ec));
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
g.wait(ec);
|
||||
g.wait();
|
||||
|
||||
BOOST_CHECK(!c1.running());
|
||||
BOOST_CHECK(!c2.running());
|
||||
@@ -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", "5",
|
||||
"--wait", "4",
|
||||
g,
|
||||
ec
|
||||
);
|
||||
|
||||
@@ -97,8 +97,7 @@ 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(::GetCurrentProcess(), ec));
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(!bt::is_stream_handle(thr.native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
thr.join();
|
||||
#else
|
||||
# if defined(TFD_CLOEXEC) //check timer
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#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>
|
||||
@@ -149,8 +148,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else if (vm["loop"].as<bool>())
|
||||
{
|
||||
while (true)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
while (true);
|
||||
}
|
||||
else if (vm["abort"].as<bool>())
|
||||
{
|
||||
|
||||
@@ -24,6 +24,15 @@ 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(
|
||||
@@ -35,14 +44,31 @@ 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(25));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
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;
|
||||
@@ -53,10 +79,17 @@ 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(25));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
BOOST_CHECK(!c.running());
|
||||
|
||||
done.store(true);
|
||||
thr.join();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ if [ os.name ] = NT
|
||||
lib Advapi32 ;
|
||||
lib Ntdll ;
|
||||
lib user32 ;
|
||||
lib Bcrypt ;
|
||||
}
|
||||
|
||||
project : requirements
|
||||
@@ -30,7 +29,6 @@ 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
|
||||
;
|
||||
|
||||
@@ -61,11 +59,11 @@ test-suite standalone :
|
||||
[ run utf8.cpp test_impl ]
|
||||
[ run cstring_ref.cpp test_impl ]
|
||||
[ run environment.cpp test_impl ]
|
||||
[ run pid.cpp test_impl : : : <target-os>darwin:<build>no ]
|
||||
[ run shell.cpp test_impl ]
|
||||
;
|
||||
|
||||
test-suite with_target :
|
||||
[ run pid.cpp test_impl : --log_level=all --catch_system_errors=no -- : target ]
|
||||
[ run process.cpp test_impl : --log_level=all --catch_system_errors=no -- : target ]
|
||||
[ run windows.cpp test_impl : --log_level=all --catch_system_errors=no -- : target : <build>no <target-os>windows:<build>yes <target-os>windows:<source>Advapi32 ]
|
||||
[ run ext.cpp test_impl : --log_level=all --catch_system_errors=no -- : target : <target-os>darwin:<build>no ]
|
||||
|
||||
@@ -62,8 +62,7 @@ BOOST_AUTO_TEST_CASE(environment)
|
||||
ec.clear();
|
||||
|
||||
for (auto && ke : bpe::current())
|
||||
if (!std::get<1>(ke).empty())
|
||||
BOOST_CHECK_EQUAL(bpe::get(std::get<0>(ke)), std::get<1>(ke));
|
||||
BOOST_CHECK_EQUAL(bpe::get(std::get<0>(ke)), std::get<1>(ke));
|
||||
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
@@ -151,14 +150,11 @@ 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"));
|
||||
|
||||
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(cmd_exe)
|
||||
BOOST_CHECK_EQUAL(bp2::detail::conv_string<char>(ref.data(), ref.size()), pth);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(cm.argc(), args.size() + 1);
|
||||
for (auto i = 0u; i < args.size(); i++)
|
||||
for (auto i = 0; i < args.size(); i++)
|
||||
{
|
||||
ref = cm.argv()[i + 1];
|
||||
|
||||
|
||||
@@ -5,12 +5,10 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
#include <boost/process/v2/process.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_pid)
|
||||
@@ -24,33 +22,21 @@ BOOST_AUTO_TEST_CASE(test_pid)
|
||||
BOOST_CHECK_GT(all.size(), 0u);
|
||||
BOOST_CHECK(itr != all.end());
|
||||
|
||||
std::vector<bp2::pid_type> children, grand_children;
|
||||
auto grand_child_pids = [](bp2::pid_type pid,
|
||||
std::vector<bp2::pid_type> & children,
|
||||
std::vector<bp2::pid_type> & grand_children)
|
||||
{
|
||||
children = bp2::child_pids(pid);
|
||||
for (unsigned i = 0; i < children.size(); i++)
|
||||
{
|
||||
std::vector<bp2::pid_type> tmp1;
|
||||
std::vector<bp2::pid_type> tmp2 = bp2::child_pids(children[i]);
|
||||
tmp1.insert(std::end(tmp1), std::begin(tmp2), std::end(tmp2));
|
||||
grand_children = tmp1;
|
||||
}
|
||||
return (!children.empty() || !grand_children.empty());
|
||||
};
|
||||
BOOST_CHECK_NE(grand_child_pids(bp2::root_pid, children, grand_children), false);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(child_pid)
|
||||
{
|
||||
namespace bp2 = boost::process::v2;
|
||||
|
||||
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(100));
|
||||
|
||||
auto cs = bp2::child_pids(bp2::current_pid());
|
||||
boost::asio::io_context ctx;
|
||||
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_LE(cs.size(), c2.size());
|
||||
BOOST_CHECK(std::find(cs.begin(), cs.end(), proc.id()) == cs.end());
|
||||
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_LE(c3.size(), c2.size());
|
||||
}
|
||||
@@ -26,19 +26,12 @@
|
||||
#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>
|
||||
|
||||
@@ -48,6 +41,35 @@
|
||||
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);
|
||||
|
||||
@@ -65,6 +87,7 @@ 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);
|
||||
@@ -122,69 +145,52 @@ 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];
|
||||
|
||||
|
||||
bpv::process proc(ctx, pth, {"sleep", "10"});
|
||||
auto sh = shell();
|
||||
|
||||
BOOST_CHECK_MESSAGE(!sh.empty(), sh);
|
||||
bpv::process proc(ctx, sh, {});
|
||||
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;
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
auto sh = closable();
|
||||
BOOST_CHECK_MESSAGE(!sh.empty(), sh);
|
||||
|
||||
asio::readable_pipe rp{ctx};
|
||||
asio::writable_pipe wp{ctx};
|
||||
asio::connect_pipe(rp, wp);
|
||||
|
||||
bpv::process proc(ctx, pth, {"sigterm"}
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
, bpv::windows::show_window_minimized_not_active
|
||||
, bpv::windows::create_new_console
|
||||
bpv::process proc(ctx, sh, {}, bpv::process_stdio{wp}
|
||||
#if defined(ASIO_WINDOWS)
|
||||
, asio::windows::show_window_minimized_not_active
|
||||
#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];
|
||||
|
||||
|
||||
bpv::process proc(ctx, pth, {"sigint"}
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
, bpv::windows::create_new_process_group
|
||||
auto sh = interruptable();
|
||||
BOOST_CHECK_MESSAGE(!sh.empty(), sh);
|
||||
bpv::process proc(ctx, sh, {}
|
||||
#if defined(ASIO_WINDOWS)
|
||||
, asio::windows::create_new_process_group
|
||||
#endif
|
||||
);
|
||||
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.interrupt();
|
||||
proc.wait();
|
||||
BOOST_CHECK_EQUAL(proc.exit_code() & ~SIGTERM, 0);
|
||||
}
|
||||
|
||||
void trim_end(std::string & str)
|
||||
@@ -365,26 +371,27 @@ 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"});
|
||||
|
||||
BOOST_CHECK_EQUAL(asio::write(proc, asio::buffer("FOOBAR", 6)), 6);
|
||||
|
||||
asio::write(proc, asio::buffer("FOOBAR"));
|
||||
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(proc, asio::dynamic_buffer(res), ec);
|
||||
n += asio::read(rp, 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());
|
||||
}
|
||||
@@ -417,11 +424,6 @@ BOOST_AUTO_TEST_CASE(print_other_cwd)
|
||||
|
||||
BOOST_CHECK(sz != 0);
|
||||
BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message());
|
||||
|
||||
|
||||
if (out.back() != '/' && target.string().back() == '/')
|
||||
out += '/';
|
||||
|
||||
BOOST_CHECK_MESSAGE(bpv::filesystem::path(out) == target,
|
||||
bpv::filesystem::path(out) << " != " << target);
|
||||
|
||||
@@ -513,34 +515,13 @@ BOOST_AUTO_TEST_CASE(exit_code_as_error)
|
||||
|
||||
proc3.terminate();
|
||||
|
||||
|
||||
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());
|
||||
});
|
||||
|
||||
proc1.async_wait(bpv::code_as_error([&](bpv::error_code ec){called ++; BOOST_CHECK(!ec);}));
|
||||
proc2.async_wait(bpv::code_as_error([&](bpv::error_code ec){called ++; BOOST_CHECK_MESSAGE(ec, ec.message());}));
|
||||
proc3.async_wait(bpv::code_as_error([&](bpv::error_code ec){called ++; BOOST_CHECK_MESSAGE(ec, ec.message());}));
|
||||
|
||||
ctx.run();
|
||||
BOOST_CHECK_EQUAL(called, 3);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bind_launcher)
|
||||
@@ -551,13 +532,17 @@ 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=*/rp});
|
||||
bpv::process proc = l(ctx, pth, args, bpv::process_stdio{/*.in=*/{}, /*.out=*/wp});
|
||||
wp.close();
|
||||
|
||||
std::string out;
|
||||
bpv::error_code ec;
|
||||
@@ -566,12 +551,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 += '/';
|
||||
|
||||
BOOST_CHECK_MESSAGE(bpv::filesystem::path(out) == target,
|
||||
bpv::filesystem::path(out) << " != " << target);
|
||||
|
||||
@@ -579,68 +560,5 @@ 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(50)};
|
||||
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,7 +1,6 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/process/v2/environment.hpp>
|
||||
|
||||
extern char **environ;
|
||||
@@ -72,71 +71,15 @@ int main(int argc, char * argv[])
|
||||
GetStartupInfo(&si);
|
||||
return static_cast<int>(si.wShowWindow);
|
||||
}
|
||||
#endif
|
||||
else if (mode == "sigterm")
|
||||
else if (mode == "creation-flags")
|
||||
{
|
||||
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;
|
||||
STARTUPINFO si;
|
||||
GetStartupInfo(&si);
|
||||
return static_cast<int>(si.dwFlags);
|
||||
}
|
||||
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,6 +50,19 @@ 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