mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
227 Commits
boost-1.70
...
boost-1.74
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2a96a3e13 | ||
|
|
1dbb3626a9 | ||
|
|
d79e1f2443 | ||
|
|
605dcd19d8 | ||
|
|
6b6a6fa61c | ||
|
|
295e2bdd9c | ||
|
|
dd1513846b | ||
|
|
7a94abfaf2 | ||
|
|
b55a09479c | ||
|
|
5afb20760c | ||
|
|
eec87e1dd9 | ||
|
|
f250a33fb4 | ||
|
|
570cf83a96 | ||
|
|
d52d244f83 | ||
|
|
04ab646f12 | ||
|
|
31c65b5442 | ||
|
|
ebbb6d8b36 | ||
|
|
b9c0140a26 | ||
|
|
27f587a4be | ||
|
|
7f6061c956 | ||
|
|
2f32c95341 | ||
|
|
a8029fc191 | ||
|
|
9f4bd9bce3 | ||
|
|
71f844c24f | ||
|
|
b0b6d67e6f | ||
|
|
3a2576a4d8 | ||
|
|
34eaa262dd | ||
|
|
ce3b3d8f99 | ||
|
|
429f2ba95c | ||
|
|
0c2e7387c8 | ||
|
|
2a2ea4b92d | ||
|
|
873ab2558d | ||
|
|
03571d4eaf | ||
|
|
46ab3ba9b8 | ||
|
|
3aba1f6eb1 | ||
|
|
44771769fa | ||
|
|
8704416941 | ||
|
|
f48392399f | ||
|
|
80f81117aa | ||
|
|
9cff55215d | ||
|
|
2e4b3c2406 | ||
|
|
b510b6a9d9 | ||
|
|
046b96186f | ||
|
|
1df2e67bc4 | ||
|
|
6cf69e2797 | ||
|
|
d3e4cbf3b3 | ||
|
|
e67e49c891 | ||
|
|
29a43b17e4 | ||
|
|
6d10c3a807 | ||
|
|
668579ed6f | ||
|
|
8af828cd43 | ||
|
|
590cc10b42 | ||
|
|
3bc11ce3ac | ||
|
|
603441ecc3 | ||
|
|
612a953369 | ||
|
|
1554773d39 | ||
|
|
dd003bf2b0 | ||
|
|
0341e08297 | ||
|
|
5853345715 | ||
|
|
41f8b1cf00 | ||
|
|
56ae00c7a4 | ||
|
|
f58882c956 | ||
|
|
3f14ebc755 | ||
|
|
1502de1001 | ||
|
|
8541cae396 | ||
|
|
38fa1fd040 | ||
|
|
ba15f760ab | ||
|
|
5e3e8f977e | ||
|
|
6182876d4f | ||
|
|
5bfd2ee08c | ||
|
|
c91b227c47 | ||
|
|
6a4d2ff721 | ||
|
|
6bf37ea8e8 | ||
|
|
ad38cdfada | ||
|
|
167ee79fa9 | ||
|
|
6b83d0b9dd | ||
|
|
16d16d40be | ||
|
|
f5f0866745 | ||
|
|
408cff1997 | ||
|
|
09faec4732 | ||
|
|
5ab43529b7 | ||
|
|
97f5b3c049 | ||
|
|
6dd3e0bdb4 | ||
|
|
eba5cb7be2 | ||
|
|
f4c51bcd5a | ||
|
|
410c0d592e | ||
|
|
40df7899b2 | ||
|
|
51083a8fa8 | ||
|
|
fe3cb0efc7 | ||
|
|
e0dd3b9658 | ||
|
|
d7d84f3952 | ||
|
|
6ccce9104a | ||
|
|
984c0c5b71 | ||
|
|
43523fcf8b | ||
|
|
cf7ad36438 | ||
|
|
ca994c1972 | ||
|
|
0a554c92b5 | ||
|
|
fa81cecffc | ||
|
|
977b76f6aa | ||
|
|
fc1acb82d9 | ||
|
|
db7af9f87d | ||
|
|
a350cc346b | ||
|
|
9fa86d3d65 | ||
|
|
e426f2bfac | ||
|
|
6b173117aa | ||
|
|
ecbc93408f | ||
|
|
6ba9a48d15 | ||
|
|
519c0a636a | ||
|
|
82195c61af | ||
|
|
c604e3a20e | ||
|
|
b27d0170ba | ||
|
|
98fd4eecf0 | ||
|
|
3799315ce7 | ||
|
|
b9431ba492 | ||
|
|
c0dca35615 | ||
|
|
790d79db9c | ||
|
|
5de0a795d1 | ||
|
|
cbaa913e3d | ||
|
|
5786162fb5 | ||
|
|
78c44dd560 | ||
|
|
23ff67d83d | ||
|
|
476c6ccd95 | ||
|
|
28126b3432 | ||
|
|
ed8388d091 | ||
|
|
43c402a5da | ||
|
|
9ff2f6f3ef | ||
|
|
b2a0fadaca | ||
|
|
3c4057204e | ||
|
|
717ac47510 | ||
|
|
4f6f4eb391 | ||
|
|
99e04036c7 | ||
|
|
519e339365 | ||
|
|
d2c930470f | ||
|
|
d5709ae747 | ||
|
|
d2ab81b1b9 | ||
|
|
885557fe01 | ||
|
|
b5b758f89a | ||
|
|
2a954eb809 | ||
|
|
0edff5449a | ||
|
|
417ea77f2f | ||
|
|
9e3fdc9669 | ||
|
|
66c2867371 | ||
|
|
caa7b2fcc8 | ||
|
|
6263e74bcd | ||
|
|
faae08ee64 | ||
|
|
2314e19f12 | ||
|
|
cfd0fc055c | ||
|
|
849b5d0f30 | ||
|
|
d13df2a194 | ||
|
|
86fc3b0b4d | ||
|
|
4733ca719f | ||
|
|
296f12eb64 | ||
|
|
96d3470e37 | ||
|
|
2265c98d81 | ||
|
|
060e5c2526 | ||
|
|
b4894807f1 | ||
|
|
2aa5e1461c | ||
|
|
61fa15fa48 | ||
|
|
92508e06a1 | ||
|
|
e85f0d0816 | ||
|
|
5e90c8de9b | ||
|
|
a486a25a07 | ||
|
|
f8c0dd4da5 | ||
|
|
ee6870bfbc | ||
|
|
0422b6bfb8 | ||
|
|
7fc41b2815 | ||
|
|
a49f1f6e2d | ||
|
|
af54484bc2 | ||
|
|
ebcb30e4bd | ||
|
|
5371c9813b | ||
|
|
9a833c610d | ||
|
|
c99ebfee7a | ||
|
|
34861366e0 | ||
|
|
e6722c452c | ||
|
|
b1e38842fb | ||
|
|
bcc9826e67 | ||
|
|
b6c6753b87 | ||
|
|
0d008a88fc | ||
|
|
f92ec53968 | ||
|
|
00a87d0a67 | ||
|
|
462334639c | ||
|
|
7129182044 | ||
|
|
03fbed44ad | ||
|
|
0277c4fcec | ||
|
|
4bc1ae6ff8 | ||
|
|
266c4503aa | ||
|
|
80479b6b70 | ||
|
|
823a346a08 | ||
|
|
8f3a7b9c63 | ||
|
|
2ebcc07892 | ||
|
|
a673cd9643 | ||
|
|
12971db132 | ||
|
|
94be279992 | ||
|
|
12cded5995 | ||
|
|
0c16a0e5b3 | ||
|
|
e4a6fde7b9 | ||
|
|
92d2bebaaf | ||
|
|
7e7a8cbc1d | ||
|
|
4f767f4bfe | ||
|
|
63f714ae2f | ||
|
|
1b3b9b707c | ||
|
|
f64bc8a6d4 | ||
|
|
102834130d | ||
|
|
fdc6a11cbc | ||
|
|
19b20f55ce | ||
|
|
60d072ce46 | ||
|
|
34f05b9276 | ||
|
|
1b476b0430 | ||
|
|
b294710e60 | ||
|
|
f8cd325d1b | ||
|
|
2a6c23e173 | ||
|
|
d7768b7221 | ||
|
|
b8821eac57 | ||
|
|
d20b64cf37 | ||
|
|
f0ddd6ca29 | ||
|
|
574d9e09d6 | ||
|
|
7085a50f36 | ||
|
|
0485459da2 | ||
|
|
318439af2e | ||
|
|
4d52ff362f | ||
|
|
8d48d9cbfa | ||
|
|
f25c31c847 | ||
|
|
192191ecfb | ||
|
|
3aeb6601cd | ||
|
|
8a7c37617e | ||
|
|
1b6ccb6d39 | ||
|
|
6abce365c5 |
75
.circleci/config.yml
Normal file
75
.circleci/config.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
environment:
|
||||
- BOOST_LIBRARY=process
|
||||
- CXX_STANDARD=gnu++11
|
||||
docker:
|
||||
- image: gcc:7
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Setting up Environment
|
||||
command: |
|
||||
echo 'export BOOST="$HOME/boost-local"' >> $BASH_ENV
|
||||
if [ $CIRCLE_BRANCH = "master" ]; then
|
||||
echo 'export BOOST_BRANCH="master"' >> $BASH_ENV;
|
||||
else
|
||||
echo 'export BOOST_BRANCH="develop"' >> $BASH_ENV;
|
||||
fi
|
||||
echo 'export BOOST_REMOVE="$BOOST/libs/$BOOST_LIBRARY"' >> $BASH_ENV
|
||||
HOME_SED_=$(echo $HOME | sed -e 's/\//\\\//g')
|
||||
echo 'export HOME_SED=$HOME_SED_' >> $BASH_ENV
|
||||
- run:
|
||||
name: install pre dependencies
|
||||
command: |
|
||||
apt-get update -yqq
|
||||
apt-get install git curl valgrind -y
|
||||
- run:
|
||||
name: Initializing git repo for boost
|
||||
command: |
|
||||
git init $BOOST
|
||||
cd $BOOST
|
||||
echo Testing $BRANCH_TO_TEST
|
||||
git remote add --no-tags -t $BOOST_BRANCH origin https://github.com/boostorg/boost.git
|
||||
git fetch --depth=1
|
||||
git checkout $BOOST_BRANCH
|
||||
git submodule update --init --merge
|
||||
git remote set-branches --add origin $BOOST_BRANCH
|
||||
git pull --recurse-submodules
|
||||
git submodule update --init
|
||||
git checkout $BOOST_BRANCH
|
||||
git submodule foreach "git reset --quiet --hard; git clean -fxd"
|
||||
git reset --hard; git clean -fxd
|
||||
git status
|
||||
rm -rf $BOOST_REMOVE
|
||||
mv $HOME/project $BOOST_REMOVE
|
||||
- run:
|
||||
name: Bootstrapping boost-build
|
||||
command: |
|
||||
cd $BOOST
|
||||
./bootstrap.sh
|
||||
./b2 headers
|
||||
- run:
|
||||
name: Building examples
|
||||
command: |
|
||||
cd $BOOST_REMOVE/example
|
||||
../../../b2 -j2 address-model=64 architecture=x86 toolset=gcc cxxflags="-std=gnu++14" -sBOOST_BUILD_PATH=. | tee example.log || FAILED=1
|
||||
sed -i -e "s/^..\/..\/..\/boost\/process\//\/root\/project\/include\/boost\/process\//gm" example.log
|
||||
python <(curl -s https://report.ci/annotate.py) --tool gcc --name "Circle CI Gcc Build" --input example.log
|
||||
exit $FAILED
|
||||
- run:
|
||||
name: Running Unit tests
|
||||
command: |
|
||||
cd $BOOST_REMOVE/test
|
||||
../../../b2 -j2 with-valgrind address-model=64 architecture=x86 testing.launcher=valgrind valgrind=on toolset=gcc cxxflags="--coverage -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. | tee test.log || FAILED=1
|
||||
../../../b2 -j2 without-valgrind address-model=64 architecture=x86 toolset=gcc cxxflags="--coverage -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. | tee no-valgrind.log || FAILED=1
|
||||
sed -i -e "s/^..\/..\/..\/boost\/process\//\/root\/project\/include\/boost\/process\//gm" test.log
|
||||
sed -i -e "s/^..\/..\/..\/boost\/process\//\/root\/project\/include\/boost\/process\//gm" no-valgrind.log
|
||||
|
||||
python <(curl -s https://report.ci/annotate.py) --tool gcc --input test.log
|
||||
python <(curl -s https://report.ci/annotate.py) --tool gcc --input no-valgrind.log
|
||||
bash <(curl -s https://codecov.io/bash) -x gcov > /dev/null || true
|
||||
echo "BUILD_RESULT: $FAILED"
|
||||
exit $FAILED
|
||||
14
.travis.yml
14
.travis.yml
@@ -23,8 +23,6 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
secure: "vs7qgXb0lQg8CTyDPSi3RQtOIOtssaCkBIx86UoEvTXwJCTOLPe7ZufQ0lobn0OVWo261AMx9GbumBBzqfsvJc1G6ixGBVwymiGli/R8DZDvvg9UdljsEk65s/XbujE/9qh97zKGGioFyCn1Bmf5+SdDAxsuXTZm/cBny5VxYaaCR7s2cFUmp4up/djqg1GI7uwBh3ceodT3OL1X3dlMV59gOJWWNsB+RO9b9DPhTW7nOlMNRiEFik4rweecQB0JS8LaHDjYwzIRrGYHX+lR9cE/O8GCCHcUOmq9jCozDdxx+HZRu4rb1ST1RiDbvYaoeTif0Df1fVXHWOoO2D4NlXB6tJPXw2mkop00j6zkcydUJYid6T1lwfEpXAhd5A9FvOIXO5hoju1wlqfkU2eFQ9Na8z8bCIX2niZmveZWp4Ag52gEPzJMFx9hHGT8J4FWMvkqTWezux1sPZrjZjc0kXdJrIp84D9MsBc1sKrxOAOb5ekSfIK5n4JDkgUtuwMSTvEdWqNJXFPZq1rEu4GTwX99z3/XF+pM5XaCDQtZ/zUA5SPHhy0dKLH/BvceUqLJt53+lMcpsltJDB+XxQ/CFL7IdgR91OKGus/z4dbVWiSdkoNvcuZqjQLFLOMVNxoqC6PRvDAEhpy21j/5GUPvM5baQS7IEin0NF7bOTtXJdY="
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- BADGE=linux
|
||||
@@ -73,7 +71,7 @@ before_install:
|
||||
# Set this to the name of the library
|
||||
- PROJECT_TO_TEST=`basename $TRAVIS_BUILD_DIR`
|
||||
- echo "Testing $PROJECT_TO_TEST"
|
||||
- if [ $TRAVIS_OS_NAME = "osx" ]; then brew install gcc5; brew install valgrind; brew install llvm; TOOLSET=clang; BOOST_TEST_CATCH_SYSTEM_ERRORS=no; MULTITHREAD=-j8; else TOOLSET=gcc-5; USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
|
||||
- if [ $TRAVIS_OS_NAME = "osx" ]; then brew install gcc5; brew install valgrind; brew install llvm; TOOLSET=clang; BOOST_TEST_CATCH_SYSTEM_ERRORS=no; MULTITHREAD=-j8; else TOOLSET=gcc-5; REPORT_CI=--boost-process-report-ci USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
|
||||
# Cloning Boost libraries (fast nondeep cloning)
|
||||
- BOOST=$HOME/boost-local
|
||||
- git init $BOOST
|
||||
@@ -87,7 +85,7 @@ before_install:
|
||||
- git checkout $BOOST_BRANCH
|
||||
- git submodule update --init --merge
|
||||
- git remote set-branches --add origin $BOOST_BRANCH
|
||||
- git pull --recurse-submodules
|
||||
- git pull --recurse-submodules || true
|
||||
- git submodule update --init
|
||||
- git checkout $BOOST_BRANCH
|
||||
- git submodule foreach "git reset --quiet --hard; git clean -fxd"
|
||||
@@ -103,8 +101,8 @@ before_install:
|
||||
- echo BOOST_TEST_CATCH_SYSTEM_ERRORS $BOOST_TEST_CATCH_SYSTEM_ERRORS
|
||||
script:
|
||||
# `--coverage` flags required to generate coverage info for Coveralls
|
||||
- ../../../b2 $MULTITHREAD with-valgrind address-model=64 architecture=x86 $USE_VALGRIND toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
|
||||
- ../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
|
||||
- ../../../b2 $MULTITHREAD with-valgrind address-model=64 architecture=x86 $USE_VALGRIND toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI
|
||||
- ../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI
|
||||
after_success:
|
||||
# Copying Coveralls data to a separate folder
|
||||
- mkdir -p $TRAVIS_BUILD_DIR/coverals
|
||||
@@ -133,4 +131,6 @@ after_success:
|
||||
- coveralls-lcov coverals/coverage.info
|
||||
|
||||
after_script:
|
||||
- curl -s https://report.ci/upload.py | python - --token=$REPORT_CI_TOKEN --name="$BADGE test run"
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- cd $BOOST/libs/$PROJECT_TO_TEST/test
|
||||
- curl -s https://report.ci/report.py | python - --name="$BADGE test run"
|
||||
|
||||
@@ -16,11 +16,11 @@ In that it is different than other facilities (like sockets) and provides anothe
|
||||
Pipes are typically used for interprocess communication. The main reason is, that pipes can be directly assigned to the process stdio, i.e. stderr, stdin and stdout.
|
||||
Additionally, half of the pipe can be inherited to the child process and closed in the father process. This will cause the pipe to be broken when the child process exits.
|
||||
|
||||
Though please not, that if the the same thread reads and write to a pipe, it will only talk to itself.
|
||||
Though please note, that if the same thread reads and writes to a pipe, it will only talk to itself.
|
||||
|
||||
[section:anonymous Anonymous Pipes]
|
||||
|
||||
The usual type of pipes, are the anonymous ones. Since the have no name,
|
||||
The most common pipes are anonymous. Since they have no name,
|
||||
a handle to them can only be obtained from duplicating either handle.
|
||||
|
||||
In this library the following functions are used for the creation of unnamed pipes:
|
||||
@@ -57,12 +57,12 @@ Every process is identified by a unique number[footnote it is unique as long as
|
||||
|
||||
[section:exit_code Exit code]
|
||||
A process will return an integer value indicating whether it was successful. On posix
|
||||
there are more codes associated with that, but not so on windows. Therefore there is not such encoding currently in the library.
|
||||
there are more codes associated with that, but not so on windows. Therefore there is no such encoding currently in the library.
|
||||
However an exit code of zero means the process was successful, while one different than zero indicates an error.
|
||||
[endsect]
|
||||
|
||||
[section:termination Termination]
|
||||
Processes can also be forced to exit. There are two ways to do this, signal the process to so and wait, and just terminate the process without conditions.
|
||||
Processes can also be forced to exit. There are two ways to do this, signal the process to do so and wait, and just terminate the process without conditions.
|
||||
|
||||
Usually the first approach is to signal an exit request, but windows - unlike posix - does not provide a consistent way to do this. Hence this is not part of the
|
||||
library and only the hard terminate is.
|
||||
@@ -79,4 +79,4 @@ The environment is a map of variables local to every process. The most significa
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
[endsect]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[section:design Design Rationale]
|
||||
[section Scope]
|
||||
This library is meant to give an wrapper around the different OS-specific methods
|
||||
This library is meant to give a wrapper around the different OS-specific methods
|
||||
to launch processes. Its aim is to provide all functionality that is available on
|
||||
those systems and allow the user to do all related things, which require using the OS APIs.
|
||||
|
||||
[*This library does not try to provide a full library for everything process related]
|
||||
[*This library does not try to provide a full library for everything process related.]
|
||||
In many discussions the proposal was made to build boost.process into a DSEL [footnote Domain Specific Embedded Language] of some sort.
|
||||
This is not the goal, it rather provides the facilities to build such a DSEL-Library on top of it.
|
||||
This is not the goal, it rather provides the facilities to build such a DSEL-library on top of it.
|
||||
Therefore the library also does [*not] force any particular use (such as only asynchronous communication) on its user.
|
||||
It rather could be integrated with such a library.
|
||||
|
||||
@@ -33,7 +33,7 @@ Both styles can also be mixed in some cases.
|
||||
system("gcc", "-c", args+={"main.cpp"});
|
||||
```
|
||||
|
||||
In the following section the avaible styles will be described. Note that the
|
||||
In the following section the available styles will be described. Note that the
|
||||
overload style is implemented via type traits, so the types will be listed.
|
||||
|
||||
[caution There is no guarantee in which order the arguments will be applied!
|
||||
@@ -54,7 +54,7 @@ interpret each string as an argument.
|
||||
]
|
||||
|
||||
When using the overloading variant, a single string will result in a cmd interpretation,
|
||||
several strings will yield a exe-args interpretation. Both version can be set explicitly:
|
||||
several strings will yield a exe-args interpretation. Both versions can be set explicitly:
|
||||
|
||||
```
|
||||
system("grep -c false /etc/passwd"); //cmd style
|
||||
@@ -65,7 +65,7 @@ system(exe="grep", args={"-c", "false", "/etc/passwd"}); //exe-/args-
|
||||
```
|
||||
|
||||
[note If a '"' sign is used in the argument style, it will be passed as part of the argument.
|
||||
If the same effect it wanted with the cmd syntax, it ought to be escaped, i.e. '\\\"'. ]
|
||||
If the same effect is wanted with the cmd syntax, it ought to be escaped, i.e. '\\\"'. ]
|
||||
[note The `PATH` variable will automatically be searched in the command style,
|
||||
but the one of the launching process, not the one passed to the child process.]
|
||||
[endsect]
|
||||
|
||||
@@ -15,14 +15,14 @@ To extend the library, the header [headerref boost/process/extend.hpp extend] is
|
||||
|
||||
It only provides the explicit style for custom properties, but no implicit style.
|
||||
|
||||
What this means is, that a custom initializer can be implemented, a reference to which can be passed to one of the launching functions.
|
||||
If a class inherits [classref boost::process::extend::handler] it will be regarded as a initializer and thus directly put into the sequence
|
||||
What this means is, that a custom initializer can be implemented, a reference which can be passed to one of the launching functions.
|
||||
If a class inherits [classref boost::process::extend::handler] it will be regarded as an initializer and thus directly put into the sequence
|
||||
the executor gets passed.
|
||||
|
||||
[section:structure Structure]
|
||||
|
||||
The executor calls different handlers of the initializers during the process launch.
|
||||
The basic structure is consists of three functions, as given below:
|
||||
The basic structure consists of three functions, as given below:
|
||||
|
||||
* [globalref boost::process::extend::on_setup on_setup]
|
||||
* [globalref boost::process::extend::on_error on_error]
|
||||
@@ -53,7 +53,7 @@ namespace ex = bp::extend;
|
||||
__child__ c("foo", __on_success__=[](auto & exec) {std::cout << "hello world" << std::endl;});
|
||||
```
|
||||
|
||||
Considering that lambda can also capture values, data can easily be shared between handlers.
|
||||
Considering that lambdas can also capture values, data can easily be shared between handlers.
|
||||
|
||||
To see which members the executor has, refer to [classref boost::process::extend::windows_executor windows_executor]
|
||||
and [classref boost::process::extend::posix_executor posix_executor].
|
||||
@@ -86,7 +86,7 @@ __child__ c("foo", hello_world());
|
||||
|
||||
[note The implementation is done via overloading, not overriding.]
|
||||
|
||||
Every handler not implemented dafaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event.
|
||||
Every handler not implemented defaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event.
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -100,7 +100,7 @@ this functionality is also available for extensions. If the class needs the __io
|
||||
```
|
||||
struct async_foo : __handler__, __require_io_context__
|
||||
{
|
||||
tempalte<typename Executor>
|
||||
template<typename Executor>
|
||||
void on_setup(Executor & exec)
|
||||
{
|
||||
__io_context__ & ios = __get_io_context__(exec.seq); //gives us a reference and a compiler error if not present.
|
||||
@@ -139,10 +139,10 @@ struct async_bar : __handler, __async_handler__
|
||||
|
||||
[section:error Error handling]
|
||||
|
||||
If an error occurs in the initializers it shall be told to the executor and not handles directly. This is because
|
||||
the behaviour can be changed through arguments passed to the launching function. Hence the the executor
|
||||
If an error occurs in the initializers it shall be told to the executor and not handled directly. This is because
|
||||
the behaviour can be changed through arguments passed to the launching function. Hence the executor
|
||||
has the function `set_error`, which takes an [@http://en.cppreference.com/w/cpp/error/error_code std::error_code] and a string.
|
||||
Depending on the cofiguration of the executor, this may either throw, set an internal `error_code`, or do nothing.
|
||||
Depending on the configuration of the executor, this may either throw, set an internal `error_code`, or do nothing.
|
||||
|
||||
So let's take a simple example, where we set a randomly chosen `error_code`.
|
||||
|
||||
@@ -202,8 +202,8 @@ struct hello_exe : __handler__
|
||||
};
|
||||
```
|
||||
|
||||
So given our example, the definitions with the non-native exectur are still a template so that they will not be evaluated if not used. Hence this provides a
|
||||
way to implement systems-specific code without using the preprocessor.
|
||||
So given our example, the definitions with the non-native executor are still a template so that they will not be evaluated if not used. Hence this provides a
|
||||
way to implement system-specific code without using the preprocessor.
|
||||
|
||||
[note If you only write a partial implementation, e.g. only for __posix_executor__, the other variants will default to __handler__].
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ while (is >> file)
|
||||
This will also deadlock, because the pipe does not close when the subprocess exits.
|
||||
So the `ipstream` will still look for data even though the process has ended.
|
||||
|
||||
[note It is not possible to use automatically pipe-closing in this library, because
|
||||
[note It is not possible to use automatic pipe-closing in this library, because
|
||||
a pipe might be a file-handle (as for async pipes on windows).]
|
||||
|
||||
But, since pipes are buffered, you might get incomplete data if you do this:
|
||||
@@ -64,7 +64,7 @@ while (c.running())
|
||||
}
|
||||
```
|
||||
|
||||
It is therefore highly recommended that you use the asynchronous api if you are
|
||||
It is therefore highly recommended that you use the asynchronous API if you are
|
||||
not absolutely sure how the output will look.
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -68,12 +68,12 @@ namespace bp = boost::process; //we will assume this for all further examples
|
||||
int result = bp::system("g++ main.cpp");
|
||||
```
|
||||
|
||||
If a single string (or the explicit form bp::cmd), it will be interpreted as a command line.
|
||||
If a single string is given (or the explicit form bp::cmd), it will be interpreted as a command line.
|
||||
That will cause the execution function to search the `PATH` variable to find the executable.
|
||||
The alternative is the `exe-args` style, where the first string will be interpreted as a filename (including the path),
|
||||
and the rest as arguments passed to said function.
|
||||
|
||||
[note For more details on the `cmd`/`exe-args` style look [link boost_process.design.arg_cmd_style here]]
|
||||
[note For more details on the `cmd`/`exe-args` style look [link boost_process.design.arg_cmd_style here].]
|
||||
|
||||
So as a first step, we'll use the `exe-args` style.
|
||||
|
||||
@@ -141,7 +141,7 @@ things while the process is running and afterwards get the exit code. The call
|
||||
to child_wait is necessary, to obtain it and tell the operating system, that no
|
||||
one is waiting for the process anymore.
|
||||
|
||||
[note You can also wait for a time span or a until a time point with __wait_for__ and __wait_until__]
|
||||
[note You can also wait for a time span or until a time point with __wait_for__ and __wait_until__.]
|
||||
|
||||
[warning If you don't call wait on a child object, it will be terminated on destruction.
|
||||
This can be avoided by calling __detach__ beforehand]
|
||||
@@ -217,8 +217,7 @@ std::vector<std::string> read_outline(std::string & file)
|
||||
What this does is redirect the `stdout` of the process into a pipe and we read this
|
||||
synchronously.
|
||||
|
||||
[warning The pipe will cause a deadlock if you try to read after nm exited]
|
||||
[note You can do the same thing with [globalref boost::process::std_err std_err]]
|
||||
[note You can do the same thing with [globalref boost::process::std_err std_err].]
|
||||
|
||||
Now we get the name from `nm` and we might want to demangle it, so we use input and output.
|
||||
`nm` has a demangle option, but for the sake of the example, we'll use
|
||||
@@ -247,7 +246,7 @@ std::vector<std::string> read_demangled_outline(const std::string & file)
|
||||
|
||||
std::vector<std::string> outline;
|
||||
|
||||
//we just use the same pipe, so the
|
||||
//we just use the same pipe, so the output of nm is directly passed as input to c++filt
|
||||
bp::child nm(bp::search_path("nm"), file, bp::std_out > p);
|
||||
bp::child filt(bp::search_path("c++filt"), bp::std_in < p, bp::std_out > is);
|
||||
|
||||
@@ -271,7 +270,7 @@ If you are familiar with [@http://www.boost.org/doc/libs/release/libs/asio/ boos
|
||||
you can use [classref boost::process::async_pipe async_pipe] which is implemented
|
||||
as an I/O-Object and can be used like [classref boost::process::pipe pipe] as shown above.
|
||||
|
||||
Now we get back to our compiling example. `nm` we might analyze it line by line,
|
||||
Now we get back to our compiling example. For `nm` we might analyze the output line by line,
|
||||
but the compiler output will just be put into one large buffer.
|
||||
|
||||
With [@http://www.boost.org/doc/libs/release/libs/asio/ boost.asio] this is what it looks like.
|
||||
@@ -291,7 +290,7 @@ ios.run();
|
||||
int result = c.exit_code();
|
||||
```
|
||||
|
||||
To make it easier, boost.process provides simpler interface for that, so that the buffer can be passed directly,
|
||||
To make it easier, boost.process provides a simpler interface for that, so that the buffer can be passed directly,
|
||||
provided we also pass a reference to an io_service.
|
||||
|
||||
```
|
||||
@@ -305,7 +304,7 @@ int result = c.exit_code();
|
||||
```
|
||||
|
||||
[note Passing an instance of io_service to the launching function automatically cause it to wait asynchronously for the exit, so no call of
|
||||
[memberref boost::process::child::wait wait] is needed]
|
||||
[memberref boost::process::child::wait wait] is needed.]
|
||||
|
||||
To make it even easier, you can use [@http://en.cppreference.com/w/cpp/thread/future std::future] for asynchronous operations
|
||||
(you will still need to pass a reference to a io_service) to the launching function, unless you use bp::system or bp::async_system.
|
||||
@@ -332,7 +331,7 @@ auto err = data.get();
|
||||
[endsect]
|
||||
[section:group Groups]
|
||||
|
||||
When launching several processes, processes can be grouped together.
|
||||
When launching several processes, they can be grouped together.
|
||||
This will also apply for a child process, that launches other processes,
|
||||
if they do not modify the group membership. E.g. if you call `make` which
|
||||
launches other processes and call terminate on it,
|
||||
@@ -343,7 +342,7 @@ The two main reasons to use groups are:
|
||||
# Being able to terminate child processes of the child process
|
||||
# Grouping several processes into one, just so they can be terminated at once
|
||||
|
||||
If we have program like `make`, which does launch its own child processes,
|
||||
If we have a program like `make`, which does launch its own child processes,
|
||||
a call of child_terminate might not suffice. I.e. if we have a makefile launching `gcc`
|
||||
and use the following code, the `gcc` process will still run afterwards:
|
||||
|
||||
@@ -413,14 +412,14 @@ bp::system("stuff", env_);
|
||||
```
|
||||
|
||||
A more convenient way to modify the environment for the child is the
|
||||
[globalref boost::process::env env] property, which the example as following:
|
||||
[globalref boost::process::env env] property, which can be used in the example as following:
|
||||
|
||||
```
|
||||
bp::system("stuff", bp::env["VALUE_1"]="foo", bp::env["VALUE_2"]+={"bar1", "bar2"});
|
||||
|
||||
```
|
||||
|
||||
Please see to the [headerref boost/process/environment.hpp reference] for more information.
|
||||
Please see the [headerref boost/process/environment.hpp reference] for more information.
|
||||
|
||||
[endsect]
|
||||
[endsect]
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
//[intro
|
||||
#include <boost/process.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using namespace boost::process;
|
||||
|
||||
int main()
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <boost/process/error.hpp>
|
||||
#include <boost/process/exe.hpp>
|
||||
#include <boost/process/group.hpp>
|
||||
#include <boost/process/handles.hpp>
|
||||
#include <boost/process/io.hpp>
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/shell.hpp>
|
||||
|
||||
@@ -138,7 +138,7 @@ struct args_
|
||||
|
||||
arg_setter_<char, true> operator()(std::initializer_list<const char*> &&range) const
|
||||
{
|
||||
return arg_setter_<char>(range.begin(), range.end());
|
||||
return arg_setter_<char, true>(range.begin(), range.end());
|
||||
}
|
||||
arg_setter_<char, true> operator+=(std::initializer_list<const char*> &&range) const
|
||||
{
|
||||
@@ -146,11 +146,11 @@ struct args_
|
||||
}
|
||||
arg_setter_<char, false> operator= (std::initializer_list<const char*> &&range) const
|
||||
{
|
||||
return arg_setter_<char, true>(range.begin(), range.end());
|
||||
return arg_setter_<char, false>(range.begin(), range.end());
|
||||
}
|
||||
arg_setter_<char, true> operator()(std::initializer_list<std::string> &&range) const
|
||||
{
|
||||
return arg_setter_<char>(range.begin(), range.end());
|
||||
return arg_setter_<char, true>(range.begin(), range.end());
|
||||
}
|
||||
arg_setter_<char, true> operator+=(std::initializer_list<std::string> &&range) const
|
||||
{
|
||||
@@ -158,12 +158,12 @@ struct args_
|
||||
}
|
||||
arg_setter_<char, false> operator= (std::initializer_list<std::string> &&range) const
|
||||
{
|
||||
return arg_setter_<char, true>(range.begin(), range.end());
|
||||
return arg_setter_<char, false>(range.begin(), range.end());
|
||||
}
|
||||
|
||||
arg_setter_<wchar_t, true> operator()(std::initializer_list<const wchar_t*> &&range) const
|
||||
{
|
||||
return arg_setter_<wchar_t>(range.begin(), range.end());
|
||||
return arg_setter_<wchar_t, true>(range.begin(), range.end());
|
||||
}
|
||||
arg_setter_<wchar_t, true> operator+=(std::initializer_list<const wchar_t*> &&range) const
|
||||
{
|
||||
@@ -171,11 +171,11 @@ struct args_
|
||||
}
|
||||
arg_setter_<wchar_t, false> operator= (std::initializer_list<const wchar_t*> &&range) const
|
||||
{
|
||||
return arg_setter_<wchar_t, true>(range.begin(), range.end());
|
||||
return arg_setter_<wchar_t, false>(range.begin(), range.end());
|
||||
}
|
||||
arg_setter_<wchar_t, true> operator()(std::initializer_list<std::wstring> &&range) const
|
||||
{
|
||||
return arg_setter_<wchar_t>(range.begin(), range.end());
|
||||
return arg_setter_<wchar_t, true>(range.begin(), range.end());
|
||||
}
|
||||
arg_setter_<wchar_t, true> operator+=(std::initializer_list<std::wstring> &&range) const
|
||||
{
|
||||
@@ -183,7 +183,7 @@ struct args_
|
||||
}
|
||||
arg_setter_<wchar_t, false> operator= (std::initializer_list<std::wstring> &&range) const
|
||||
{
|
||||
return arg_setter_<wchar_t, true>(range.begin(), range.end());
|
||||
return arg_setter_<wchar_t, false>(range.begin(), range.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -109,10 +109,10 @@ with `function` being a callable object with the signature `(int, const std::err
|
||||
\code{.cpp}
|
||||
io_context ios;
|
||||
|
||||
child c("ls", on_exit=[](int exit, const std::error_code& ec_in){});
|
||||
child c("ls", ios, on_exit=[](int exit, const std::error_code& ec_in){});
|
||||
|
||||
std::future<int> exit_code;
|
||||
chlid c2("ls", on_exit=exit_code);
|
||||
chlid c2("ls", ios, on_exit=exit_code);
|
||||
|
||||
\endcode
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ public:
|
||||
*/
|
||||
typedef platform_specific handle_type;
|
||||
|
||||
typedef typename handle_type::executor_type executor_type;
|
||||
|
||||
/** Construct a new async_pipe, does automatically open the pipe.
|
||||
* Initializes source and sink with the same io_context.
|
||||
* @note Windows creates a named pipe here, where the name is automatically generated.
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace boost { namespace process { namespace detail {
|
||||
|
||||
struct cmd_
|
||||
{
|
||||
constexpr cmd_() {}
|
||||
constexpr cmd_() = default;
|
||||
|
||||
template<typename Char>
|
||||
inline api::cmd_setter_<Char> operator()(const Char *s) const
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
|
||||
template<typename ...Args>
|
||||
explicit child(Args&&...args);
|
||||
child() {}
|
||||
child() { } // Must be kept non defaulted for MSVC 14.1 & 14.2 #113
|
||||
child& operator=(const child&) = delete;
|
||||
child& operator=(child && lhs)
|
||||
{
|
||||
@@ -145,11 +145,12 @@ public:
|
||||
|
||||
bool running(std::error_code & ec) noexcept
|
||||
{
|
||||
if (valid() && !_exited())
|
||||
ec.clear();
|
||||
if (valid() && !_exited() && !ec)
|
||||
{
|
||||
int exit_code = 0;
|
||||
auto res = boost::process::detail::api::is_running(_child_handle, exit_code, ec);
|
||||
if (!res && !_exited())
|
||||
if (!ec && !res && !_exited())
|
||||
_exit_status->store(exit_code);
|
||||
|
||||
return res;
|
||||
@@ -159,10 +160,11 @@ public:
|
||||
|
||||
void terminate(std::error_code & ec) noexcept
|
||||
{
|
||||
if (valid() && running(ec))
|
||||
if (valid() && running(ec) && !ec)
|
||||
boost::process::detail::api::terminate(_child_handle, ec);
|
||||
|
||||
_terminated = true;
|
||||
if (!ec)
|
||||
_terminated = true;
|
||||
}
|
||||
|
||||
void wait(std::error_code & ec) noexcept
|
||||
@@ -171,7 +173,8 @@ public:
|
||||
{
|
||||
int exit_code = 0;
|
||||
boost::process::detail::api::wait(_child_handle, exit_code, ec);
|
||||
_exit_status->store(exit_code);
|
||||
if (!ec)
|
||||
_exit_status->store(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +191,7 @@ public:
|
||||
{
|
||||
int exit_code = 0;
|
||||
auto b = boost::process::detail::api::wait_until(_child_handle, exit_code, timeout_time, ec);
|
||||
if (!b)
|
||||
if (!b || ec)
|
||||
return false;
|
||||
_exit_status->store(exit_code);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define BOOST_PROCESS_DETAIL_POSIX_ASIO_FWD_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <boost/asio/ts/netfwd.hpp>
|
||||
|
||||
namespace boost { namespace asio {
|
||||
|
||||
@@ -20,39 +21,19 @@ template<typename Allocator>
|
||||
class basic_streambuf;
|
||||
|
||||
typedef basic_streambuf<std::allocator<char>> streambuf;
|
||||
class io_context;
|
||||
|
||||
class executor;
|
||||
|
||||
|
||||
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
|
||||
class signal_set_service;
|
||||
template <typename SignalSetService>
|
||||
|
||||
class basic_signal_set;
|
||||
typedef basic_signal_set<signal_set_service> signal_set;
|
||||
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
template <typename Executor>
|
||||
class basic_signal_set;
|
||||
typedef basic_signal_set<executor> signal_set;
|
||||
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
typedef basic_signal_set<any_io_executor> signal_set;
|
||||
|
||||
template <typename Handler>
|
||||
class basic_yield_context;
|
||||
|
||||
namespace posix {
|
||||
|
||||
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
|
||||
class stream_descriptor_service;
|
||||
|
||||
template <typename StreamDesscriptorService>
|
||||
class basic_stream_descriptor;
|
||||
typedef basic_stream_descriptor<stream_descriptor_service> stream_descriptor;
|
||||
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
template <typename Executor>
|
||||
class basic_stream_descriptor;
|
||||
typedef basic_stream_descriptor<executor> stream_descriptor;
|
||||
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
typedef basic_stream_descriptor<any_io_executor> stream_descriptor;
|
||||
|
||||
} //posix
|
||||
} //asio
|
||||
|
||||
@@ -16,13 +16,16 @@
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
|
||||
template<typename Buffer>
|
||||
struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
::boost::process::detail::posix::require_io_context
|
||||
::boost::process::detail::posix::require_io_context,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
Buffer & buf;
|
||||
|
||||
@@ -33,6 +36,7 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
fut = promise->get_future(); return std::move(*this);
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<boost::process::async_pipe> pipe;
|
||||
|
||||
async_in_buffer(Buffer & buf) : buf(buf)
|
||||
@@ -76,9 +80,19 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
template<typename Executor>
|
||||
void on_setup(Executor & exec)
|
||||
{
|
||||
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
|
||||
if (!pipe)
|
||||
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
|
||||
}
|
||||
|
||||
std::array<int, 3> get_used_handles()
|
||||
{
|
||||
if (pipe)
|
||||
return {STDIN_FILENO, pipe->native_source(), pipe->native_sink()};
|
||||
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
|
||||
return {STDIN_FILENO, STDIN_FILENO, STDIN_FILENO};
|
||||
}
|
||||
|
||||
|
||||
template <typename Executor>
|
||||
void on_exec_setup(Executor &exec)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <memory>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <array>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
@@ -45,12 +47,24 @@ inline int apply_out_handles(int handle, std::integral_constant<int, 1>, std::in
|
||||
|
||||
template<int p1, int p2, typename Buffer>
|
||||
struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
|
||||
::boost::process::detail::posix::require_io_context
|
||||
::boost::process::detail::posix::require_io_context,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
Buffer & buf;
|
||||
|
||||
std::shared_ptr<boost::process::async_pipe> pipe;
|
||||
|
||||
std::array<int, 4> get_used_handles()
|
||||
{
|
||||
const auto pp1 = p1 != -1 ? p1 : p2;
|
||||
const auto pp2 = p2 != -1 ? p2 : p1;
|
||||
|
||||
if (pipe)
|
||||
return {pipe->native_source(), pipe->native_sink(), pp1, pp2};
|
||||
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
|
||||
return {pp1, pp2, pp1, pp2};
|
||||
}
|
||||
|
||||
|
||||
async_out_buffer(Buffer & buf) : buf(buf)
|
||||
{
|
||||
@@ -131,8 +145,11 @@ struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
|
||||
{
|
||||
std::istream is (buffer_.get());
|
||||
Type arg;
|
||||
arg.resize(buffer_->size());
|
||||
is.read(&*arg.begin(), buffer_->size());
|
||||
if (buffer_->size() > 0)
|
||||
{
|
||||
arg.resize(buffer_->size());
|
||||
is.read(&*arg.begin(), buffer_->size());
|
||||
}
|
||||
promise_->set_value(std::move(arg));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ class async_pipe
|
||||
public:
|
||||
typedef int native_handle_type;
|
||||
typedef ::boost::asio::posix::stream_descriptor handle_type;
|
||||
typedef typename handle_type::executor_type executor_type;
|
||||
|
||||
inline async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios) {}
|
||||
|
||||
@@ -70,10 +71,8 @@ public:
|
||||
|
||||
~async_pipe()
|
||||
{
|
||||
if (_sink .native_handle() != -1)
|
||||
::close(_sink.native_handle());
|
||||
if (_source.native_handle() != -1)
|
||||
::close(_source.native_handle());
|
||||
boost::system::error_code ec;
|
||||
close(ec);
|
||||
}
|
||||
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
|
||||
@@ -82,22 +82,30 @@ public:
|
||||
|
||||
int_type write(const char_type * data, int_type count)
|
||||
{
|
||||
auto write_len = ::write(_sink, data, count * sizeof(char_type));
|
||||
if (write_len == -1)
|
||||
::boost::process::detail::throw_last_error();
|
||||
|
||||
int_type write_len;
|
||||
while ((write_len = ::write(_sink, data, count * sizeof(char_type))) == -1)
|
||||
{
|
||||
//Try again if interrupted
|
||||
auto err = errno;
|
||||
if (err != EINTR)
|
||||
::boost::process::detail::throw_last_error();
|
||||
}
|
||||
return write_len;
|
||||
}
|
||||
int_type read(char_type * data, int_type count)
|
||||
{
|
||||
auto read_len = ::read(_source, data, count * sizeof(char_type));
|
||||
if (read_len == -1)
|
||||
::boost::process::detail::throw_last_error();
|
||||
|
||||
int_type read_len;
|
||||
while ((read_len = ::read(_source, data, count * sizeof(char_type))) == -1)
|
||||
{
|
||||
//Try again if interrupted
|
||||
auto err = errno;
|
||||
if (err != EINTR)
|
||||
::boost::process::detail::throw_last_error();
|
||||
}
|
||||
return read_len;
|
||||
}
|
||||
|
||||
bool is_open()
|
||||
bool is_open() const
|
||||
{
|
||||
return (_source != -1) ||
|
||||
(_sink != -1);
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
|
||||
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
struct close_in : handler_base_ext
|
||||
struct close_in : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
template <class Executor>
|
||||
void on_exec_setup(Executor &e) const
|
||||
@@ -23,6 +24,9 @@ struct close_in : handler_base_ext
|
||||
if (::close(STDIN_FILENO) == -1)
|
||||
e.set_error(::boost::process::detail::get_last_error(), "close() failed");
|
||||
}
|
||||
|
||||
int get_used_handles() {return STDIN_FILENO;}
|
||||
|
||||
};
|
||||
|
||||
}}}}
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
#ifndef BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
|
||||
#define BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
|
||||
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
@@ -20,6 +21,8 @@ struct close_out : handler_base_ext
|
||||
{
|
||||
template <class Executor>
|
||||
inline void on_exec_setup(Executor &e) const;
|
||||
|
||||
std::array<int, 2> get_used_handles() {return {{p1 != -1 ? p1 : p2, p2 != -1 ? p2 : p1}};}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
native_environment_impl & operator=(native_environment_impl && ) = default;
|
||||
native_handle_type _env_impl = _impl.data();
|
||||
|
||||
native_handle_type native_handle() const {return environ;}
|
||||
native_handle_type native_handle() const {return _env_impl;}
|
||||
};
|
||||
|
||||
template<>
|
||||
@@ -230,7 +230,7 @@ template<typename Char>
|
||||
inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type
|
||||
{
|
||||
auto itr = std::find_if(_data.begin(), _data.end(),
|
||||
[&](const string_type & st)
|
||||
[&](const string_type & st) -> bool
|
||||
{
|
||||
if (st.size() <= id.size())
|
||||
return false;
|
||||
@@ -250,7 +250,7 @@ template<typename Char>
|
||||
inline void basic_environment_impl<Char>::set(const string_type &id, const string_type &value)
|
||||
{
|
||||
auto itr = std::find_if(_data.begin(), _data.end(),
|
||||
[&](const string_type & st)
|
||||
[&](const string_type & st) -> bool
|
||||
{
|
||||
if (st.size() <= id.size())
|
||||
return false;
|
||||
@@ -270,7 +270,7 @@ template<typename Char>
|
||||
inline void basic_environment_impl<Char>::reset(const string_type &id)
|
||||
{
|
||||
auto itr = std::find_if(_data.begin(), _data.end(),
|
||||
[&](const string_type & st)
|
||||
[&](const string_type & st) -> bool
|
||||
{
|
||||
if (st.size() <= id.size())
|
||||
return false;
|
||||
@@ -294,7 +294,11 @@ std::vector<Char*> basic_environment_impl<Char>::_load_var(std::vector<std::basi
|
||||
ret.reserve(data.size() +1);
|
||||
|
||||
for (auto & val : data)
|
||||
{
|
||||
if (val.empty())
|
||||
val.push_back(0);
|
||||
ret.push_back(&val.front());
|
||||
}
|
||||
|
||||
ret.push_back(nullptr);
|
||||
return ret;
|
||||
|
||||
@@ -22,47 +22,12 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(__GLIBC__)
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#endif
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
inline int execvpe(const char* filename, char * const arg_list[], char* env[])
|
||||
{
|
||||
#if defined(__GLIBC__)
|
||||
return ::execvpe(filename, arg_list, env);
|
||||
#else
|
||||
//use my own implementation
|
||||
std::string fn = filename;
|
||||
if ((fn.find('/') == std::string::npos) && ::access(fn.c_str(), X_OK))
|
||||
{
|
||||
auto e = ::environ;
|
||||
while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
|
||||
e++;
|
||||
|
||||
if (e != nullptr)
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
boost::split(path, *e, boost::is_any_of(":"));
|
||||
|
||||
for (const std::string & pp : path)
|
||||
{
|
||||
auto p = pp + "/" + filename;
|
||||
if (!::access(p.c_str(), X_OK))
|
||||
{
|
||||
fn = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ::execve(fn.c_str(), arg_list, env);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
struct on_setup_t
|
||||
{
|
||||
@@ -295,15 +260,41 @@ class executor
|
||||
return;
|
||||
//EAGAIN not yet forked, EINTR interrupted, i.e. try again
|
||||
else if ((err != EAGAIN ) && (err != EINTR))
|
||||
{
|
||||
::close(source);
|
||||
set_error(std::error_code(err, std::system_category()), "Error read pipe");
|
||||
}
|
||||
}
|
||||
::close(source);
|
||||
set_error(ec, std::move(msg));
|
||||
}
|
||||
|
||||
std::string prepare_cmd_style_fn; //buffer
|
||||
|
||||
inline void prepare_cmd_style() //this does what execvpe does - but we execute it in the father process, to avoid allocations.
|
||||
{
|
||||
//use my own implementation
|
||||
prepare_cmd_style_fn = exe;
|
||||
if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK))
|
||||
{
|
||||
auto e = ::environ;
|
||||
while ((e != nullptr) && (*e != nullptr) && !boost::starts_with(*e, "PATH="))
|
||||
e++;
|
||||
|
||||
if ((e != nullptr) && (*e != nullptr))
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
boost::split(path, *e, boost::is_any_of(":"));
|
||||
|
||||
for (const std::string & pp : path)
|
||||
{
|
||||
auto p = pp + "/" + exe;
|
||||
if (!::access(p.c_str(), X_OK))
|
||||
{
|
||||
prepare_cmd_style_fn = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exe = prepare_cmd_style_fn.c_str();
|
||||
}
|
||||
|
||||
std::error_code _ec;
|
||||
std::string _msg;
|
||||
@@ -342,6 +333,8 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
|
||||
boost::fusion::for_each(seq, call_on_setup(*this));
|
||||
if (_ec)
|
||||
return child();
|
||||
if (cmd_style)
|
||||
prepare_cmd_style();
|
||||
|
||||
this->pid = ::fork();
|
||||
if (pid == -1)
|
||||
@@ -353,10 +346,7 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
|
||||
else if (pid == 0)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_exec_setup(*this));
|
||||
if (cmd_style)
|
||||
::boost::process::detail::posix::execvpe(exe, cmd_line, env);
|
||||
else
|
||||
::execve(exe, cmd_line, env);
|
||||
::execve(exe, cmd_line, env);
|
||||
auto ec = boost::process::detail::get_last_error();
|
||||
boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
|
||||
_exit(EXIT_FAILURE);
|
||||
@@ -372,71 +362,85 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
|
||||
template<typename Sequence>
|
||||
child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
|
||||
{
|
||||
int p[2];
|
||||
if (::pipe(p) == -1)
|
||||
{
|
||||
set_error(::boost::process::detail::get_last_error(), "pipe(2) failed");
|
||||
return child();
|
||||
}
|
||||
if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
auto err = ::boost::process::detail::get_last_error();
|
||||
::close(p[0]);
|
||||
::close(p[1]);
|
||||
set_error(err, "fcntl(2) failed");
|
||||
return child();
|
||||
}
|
||||
_ec.clear();
|
||||
boost::fusion::for_each(seq, call_on_setup(*this));
|
||||
struct pipe_guard
|
||||
{
|
||||
int p[2];
|
||||
pipe_guard() : p{-1,-1} {}
|
||||
|
||||
~pipe_guard()
|
||||
{
|
||||
if (p[0] != -1)
|
||||
::close(p[0]);
|
||||
if (p[1] != -1)
|
||||
::close(p[1]);
|
||||
}
|
||||
} p{};
|
||||
|
||||
if (::pipe(p.p) == -1)
|
||||
{
|
||||
set_error(::boost::process::detail::get_last_error(), "pipe(2) failed");
|
||||
return child();
|
||||
}
|
||||
if (::fcntl(p.p[1], F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
auto err = ::boost::process::detail::get_last_error();
|
||||
set_error(err, "fcntl(2) failed");//this might throw, so we need to be sure our pipe is safe.
|
||||
return child();
|
||||
}
|
||||
_ec.clear();
|
||||
boost::fusion::for_each(seq, call_on_setup(*this));
|
||||
|
||||
if (_ec)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
|
||||
if (cmd_style)
|
||||
prepare_cmd_style();
|
||||
|
||||
this->pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "fork() failed";
|
||||
boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
_pipe_sink = p.p[1];
|
||||
::close(p.p[0]);
|
||||
|
||||
boost::fusion::for_each(seq, call_on_exec_setup(*this));
|
||||
::execve(exe, cmd_line, env);
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "execve failed";
|
||||
boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
|
||||
|
||||
_write_error(_pipe_sink);
|
||||
::close(p.p[1]);
|
||||
|
||||
_exit(EXIT_FAILURE);
|
||||
return child();
|
||||
}
|
||||
|
||||
::close(p.p[1]);
|
||||
p.p[1] = -1;
|
||||
_read_error(p.p[0]);
|
||||
|
||||
}
|
||||
if (_ec)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
|
||||
this->pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "fork() failed";
|
||||
boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
|
||||
return child();
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
_pipe_sink = p[1];
|
||||
::close(p[0]);
|
||||
|
||||
boost::fusion::for_each(seq, call_on_exec_setup(*this));
|
||||
if (cmd_style)
|
||||
::boost::process::detail::posix::execvpe(exe, cmd_line, env);
|
||||
else
|
||||
::execve(exe, cmd_line, env);
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "execve failed";
|
||||
boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
|
||||
|
||||
_write_error(p[1]);
|
||||
|
||||
_exit(EXIT_FAILURE);
|
||||
return child();
|
||||
}
|
||||
|
||||
child c(child_handle(pid), exit_status);
|
||||
|
||||
::close(p[1]);
|
||||
_read_error(p[0]);
|
||||
|
||||
if (_ec)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_error(*this, _ec));
|
||||
return child();
|
||||
}
|
||||
else
|
||||
boost::fusion::for_each(seq, call_on_success(*this));
|
||||
boost::fusion::for_each(seq, call_on_success(*this));
|
||||
|
||||
if (_ec)
|
||||
{
|
||||
@@ -489,6 +493,8 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
|
||||
return child();
|
||||
}
|
||||
_ec.clear();
|
||||
if (cmd_style)
|
||||
this->prepare_cmd_style();
|
||||
|
||||
this->pid = ::vfork();
|
||||
if (pid == -1)
|
||||
@@ -504,10 +510,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
|
||||
{
|
||||
boost::fusion::for_each(seq, call_on_exec_setup(*this));
|
||||
|
||||
if (cmd_style)
|
||||
::boost::process::detail::posix::execvpe(exe, cmd_line, env);
|
||||
else
|
||||
::execve(exe, cmd_line, env);
|
||||
::execve(exe, cmd_line, env);
|
||||
|
||||
_ec = boost::process::detail::get_last_error();
|
||||
_msg = "execve failed";
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <unistd.h>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
|
||||
struct close_fd_ : handler_base_ext
|
||||
struct close_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
close_fd_(int fd) : fd_(fd) {}
|
||||
|
||||
@@ -27,12 +29,15 @@ struct close_fd_ : handler_base_ext
|
||||
e.set_error(::boost::process::detail::get_last_error(), "close() failed");
|
||||
}
|
||||
|
||||
int get_used_handles() {return fd_;}
|
||||
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
template <class Range>
|
||||
struct close_fds_ : handler_base_ext
|
||||
struct close_fds_ : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
public:
|
||||
close_fds_(const Range &fds) : fds_(fds) {}
|
||||
@@ -48,6 +53,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Range& get_used_handles() {return fds_;}
|
||||
|
||||
private:
|
||||
Range fds_;
|
||||
};
|
||||
@@ -55,7 +62,7 @@ private:
|
||||
|
||||
|
||||
template <class FileDescriptor>
|
||||
struct bind_fd_ : handler_base_ext
|
||||
struct bind_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
public:
|
||||
bind_fd_(int id, const FileDescriptor &fd) : id_(id), fd_(fd) {}
|
||||
@@ -67,6 +74,9 @@ public:
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
|
||||
}
|
||||
|
||||
std::array<int, 2> get_used_handles() {return {id_, fd_};}
|
||||
|
||||
|
||||
private:
|
||||
int id_;
|
||||
FileDescriptor fd_;
|
||||
|
||||
@@ -13,16 +13,22 @@
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/posix/file_descriptor.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
struct file_in : handler_base_ext
|
||||
struct file_in : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor file;
|
||||
int handle = file.handle();
|
||||
|
||||
std::array<int, 2> get_used_handles()
|
||||
{
|
||||
return {{STDIN_FILENO, handle}};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
file_in(T&& t) : file(std::forward<T>(t)) {}
|
||||
file_in(FILE * f) : handle(fileno(f)) {}
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/posix/file_descriptor.hpp>
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct file_out : handler_base_ext
|
||||
struct file_out : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor file;
|
||||
int handle = file.handle();
|
||||
@@ -27,6 +28,13 @@ struct file_out : handler_base_ext
|
||||
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write), handle(file.handle()) {}
|
||||
file_out(FILE * f) : handle(fileno(f)) {}
|
||||
|
||||
std::array<int, 3> get_used_handles()
|
||||
{
|
||||
const auto pp1 = p1 != -1 ? p1 : p2;
|
||||
const auto pp2 = p2 != -1 ? p2 : p1;
|
||||
|
||||
return {handle, pp1, pp2};
|
||||
}
|
||||
|
||||
template <typename Executor>
|
||||
void on_exec_setup(Executor &e) const;
|
||||
|
||||
146
include/boost/process/detail/posix/handles.hpp
Normal file
146
include/boost/process/detail/posix/handles.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2019 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
|
||||
|
||||
#include <vector>
|
||||
#include <system_error>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <algorithm>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
|
||||
using native_handle_type = int;
|
||||
|
||||
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
|
||||
{
|
||||
std::vector<native_handle_type> res;
|
||||
|
||||
std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
|
||||
if (!dir)
|
||||
{
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
return {};
|
||||
}
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
auto my_fd = ::dirfd(dir.get());
|
||||
|
||||
struct ::dirent * ent_p;
|
||||
|
||||
while ((ent_p = readdir(dir.get())) != nullptr)
|
||||
{
|
||||
if (ent_p->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
const auto conv = std::atoi(ent_p->d_name);
|
||||
if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
|
||||
continue;
|
||||
|
||||
if (conv == my_fd)
|
||||
continue;
|
||||
|
||||
res.push_back(conv);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
inline std::vector<native_handle_type> get_handles()
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
auto res = get_handles(ec);
|
||||
if (ec)
|
||||
boost::process::detail::throw_error(ec, "open_dir(\"/dev/fd\") failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
|
||||
{
|
||||
struct ::stat stat_;
|
||||
|
||||
if (::fstat(handle, &stat_) != 0)
|
||||
{
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
}
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
return S_ISCHR (stat_.st_mode) //This macro returns non-zero if the file is a character special file (a device like a terminal).
|
||||
|| S_ISBLK (stat_.st_mode) // This macro returns non-zero if the file is a block special file (a device like a disk).
|
||||
|| S_ISREG (stat_.st_mode) // This macro returns non-zero if the file is a regular file.
|
||||
|| S_ISFIFO (stat_.st_mode) // This macro returns non-zero if the file is a FIFO special file, or a pipe. See section 15. Pipes and FIFOs.
|
||||
|| S_ISSOCK (stat_.st_mode) ;// This macro returns non-zero if the file is a socket. See section 16. Sockets.;
|
||||
}
|
||||
|
||||
|
||||
inline bool is_stream_handle(native_handle_type handle)
|
||||
{
|
||||
std::error_code ec;
|
||||
auto res = is_stream_handle(handle, ec);
|
||||
if (ec)
|
||||
boost::process::detail::throw_error(ec, "fstat() failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct limit_handles_ : handler_base_ext
|
||||
{
|
||||
limit_handles_() {}
|
||||
~limit_handles_() {}
|
||||
mutable std::vector<int> used_handles;
|
||||
|
||||
template<typename Executor>
|
||||
void on_setup(Executor & exec) const
|
||||
{
|
||||
used_handles = get_used_handles(exec);
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
void on_exec_setup(Executor & exec) const
|
||||
{
|
||||
auto dir = ::opendir("/dev/fd");
|
||||
if (!dir)
|
||||
{
|
||||
exec.set_error(::boost::process::detail::get_last_error(), "opendir(\"/dev/fd\")");
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_fd = ::dirfd(dir);
|
||||
struct ::dirent * ent_p;
|
||||
|
||||
while ((ent_p = readdir(dir)) != nullptr)
|
||||
{
|
||||
if (ent_p->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
const auto conv = std::atoi(ent_p->d_name);
|
||||
|
||||
if ((conv == my_fd) || (conv == -1))
|
||||
continue;
|
||||
|
||||
if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end())
|
||||
continue;
|
||||
|
||||
if (::close(conv) != 0)
|
||||
{
|
||||
exec.set_error(::boost::process::detail::get_last_error(), "close() failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
::closedir(dir);
|
||||
}
|
||||
};
|
||||
|
||||
}}}}
|
||||
|
||||
#endif //PROCESS_HANDLES_HPP
|
||||
@@ -14,13 +14,21 @@
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/posix/file_descriptor.hpp>
|
||||
#include <unistd.h>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
struct null_in : handler_base_ext
|
||||
struct null_in : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor source{"/dev/null", file_descriptor::read};
|
||||
|
||||
std::array<int, 2> get_used_handles()
|
||||
{
|
||||
return {{STDIN_FILENO, source.handle()}};
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
template <class Executor>
|
||||
void on_exec_setup(Executor &e) const
|
||||
|
||||
@@ -13,17 +13,27 @@
|
||||
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <boost/process/detail/posix/file_descriptor.hpp>
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct null_out : handler_base_ext
|
||||
struct null_out : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor sink{"/dev/null", file_descriptor::write};
|
||||
|
||||
template <typename Executor>
|
||||
void on_exec_setup(Executor &e) const;
|
||||
|
||||
std::array<int, 3> get_used_handles()
|
||||
{
|
||||
const auto pp1 = p1 != -1 ? p1 : p2;
|
||||
const auto pp2 = p2 != -1 ? p2 : p1;
|
||||
|
||||
return {sink.handle(), pp1, pp2};
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
@@ -13,17 +13,23 @@
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/detail/posix/handler.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
struct pipe_in : handler_base_ext
|
||||
struct pipe_in : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
int source;
|
||||
int sink; //opposite end
|
||||
|
||||
pipe_in(int sink, int source) : source(source), sink(sink) {}
|
||||
|
||||
std::array<int, 3> get_used_handles()
|
||||
{
|
||||
return {{STDIN_FILENO, source, sink}};
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
pipe_in(T & p) : source(p.native_source()), sink(p.native_sink())
|
||||
@@ -48,7 +54,9 @@ struct pipe_in : handler_base_ext
|
||||
{
|
||||
if (::dup2(source, STDIN_FILENO) == -1)
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
|
||||
::close(source);
|
||||
if (source != STDIN_FILENO)
|
||||
::close(source);
|
||||
|
||||
::close(sink);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,10 @@ template<typename Executor>
|
||||
void pipe_out<1,-1>::on_exec_setup(Executor &e) const
|
||||
{
|
||||
if (::dup2(sink, STDOUT_FILENO) == -1)
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup3() failed");
|
||||
::close(sink);
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
|
||||
|
||||
if (sink != STDOUT_FILENO)
|
||||
::close(sink);
|
||||
::close(source);
|
||||
}
|
||||
|
||||
@@ -63,7 +65,9 @@ void pipe_out<2,-1>::on_exec_setup(Executor &e) const
|
||||
{
|
||||
if (::dup2(sink, STDERR_FILENO) == -1)
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
|
||||
::close(sink);
|
||||
|
||||
if (sink != STDOUT_FILENO)
|
||||
::close(sink);
|
||||
::close(source);
|
||||
}
|
||||
|
||||
@@ -75,8 +79,8 @@ void pipe_out<1,2>::on_exec_setup(Executor &e) const
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
|
||||
if (::dup2(sink, STDERR_FILENO) == -1)
|
||||
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
|
||||
::close(sink);
|
||||
::close(source);
|
||||
if ((sink != STDOUT_FILENO) && (sink != STDERR_FILENO))
|
||||
::close(sink);
|
||||
}
|
||||
|
||||
class async_pipe;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace boost { namespace process { namespace detail { namespace posix {
|
||||
|
||||
class sigchld_service : public boost::asio::detail::service_base<sigchld_service>
|
||||
{
|
||||
boost::asio::io_context::strand _strand{get_io_context()};
|
||||
boost::asio::strand<boost::asio::io_context::executor_type> _strand{get_io_context().get_executor()};
|
||||
boost::asio::signal_set _signal_set{get_io_context(), SIGCHLD};
|
||||
|
||||
std::vector<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;
|
||||
|
||||
@@ -21,7 +21,7 @@ struct start_dir_init : handler_base_ext
|
||||
{
|
||||
typedef Char value_type;
|
||||
typedef std::basic_string<value_type> string_type;
|
||||
start_dir_init(const string_type &s) : s_(s) {}
|
||||
start_dir_init(string_type s) : s_(std::move(s)) {}
|
||||
|
||||
template <class PosixExecutor>
|
||||
void on_exec_setup(PosixExecutor&) const
|
||||
|
||||
@@ -27,7 +27,7 @@ inline void terminate(const child_handle &p, std::error_code &ec) noexcept
|
||||
ec.clear();
|
||||
|
||||
int status;
|
||||
::waitpid(p.pid, &status, 0); //just to clean it up
|
||||
::waitpid(p.pid, &status, WNOHANG); //just to clean it up
|
||||
}
|
||||
|
||||
inline void terminate(const child_handle &p)
|
||||
|
||||
@@ -54,11 +54,35 @@ inline bool wait_until(
|
||||
const std::chrono::time_point<Clock, Duration>& time_out,
|
||||
std::error_code & ec) noexcept
|
||||
{
|
||||
|
||||
::sigset_t sigset;
|
||||
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGCHLD);
|
||||
//I need to set the signal, because it might be ignore / default, in which case sigwait might not work.
|
||||
|
||||
using _signal_t = void(*)(int);
|
||||
static thread_local _signal_t sigchld_handler = SIG_DFL;
|
||||
|
||||
struct signal_interceptor_t
|
||||
{
|
||||
static void handler_func(int val)
|
||||
{
|
||||
if ((sigchld_handler != SIG_DFL) && (sigchld_handler != SIG_IGN))
|
||||
sigchld_handler(val);
|
||||
}
|
||||
signal_interceptor_t() { sigchld_handler = ::signal(SIGCHLD, &handler_func); }
|
||||
~signal_interceptor_t() { ::signal(SIGCHLD, sigchld_handler); sigchld_handler = SIG_DFL;}
|
||||
|
||||
} signal_interceptor{};
|
||||
|
||||
if (sigemptyset(&sigset) != 0)
|
||||
{
|
||||
ec = get_last_error();
|
||||
return false;
|
||||
}
|
||||
if (sigaddset(&sigset, SIGCHLD) != 0)
|
||||
{
|
||||
ec = get_last_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto get_timespec =
|
||||
[](const Duration & dur)
|
||||
@@ -69,8 +93,8 @@ inline bool wait_until(
|
||||
return ts;
|
||||
};
|
||||
|
||||
pid_t ret;
|
||||
int status;
|
||||
int ret;
|
||||
int status{0};
|
||||
|
||||
struct ::sigaction old_sig;
|
||||
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
|
||||
@@ -80,6 +104,7 @@ inline bool wait_until(
|
||||
}
|
||||
|
||||
bool timed_out;
|
||||
|
||||
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
|
||||
do
|
||||
{
|
||||
@@ -113,9 +138,16 @@ inline bool wait_until(
|
||||
{
|
||||
auto ts = get_timespec(time_out - Clock::now());
|
||||
::timespec rem;
|
||||
::nanosleep(&ts, &rem);
|
||||
while (rem.tv_sec > 0 || rem.tv_nsec > 0)
|
||||
::nanosleep(&rem, &rem);
|
||||
while (ts.tv_sec > 0 || ts.tv_nsec > 0)
|
||||
{
|
||||
if (::nanosleep(&ts, &rem) != 0)
|
||||
{
|
||||
auto err = errno;
|
||||
if ((err == EINVAL) || (err == EFAULT))
|
||||
break;
|
||||
}
|
||||
ts = get_timespec(time_out - Clock::now());
|
||||
}
|
||||
::exit(0);
|
||||
}
|
||||
|
||||
@@ -125,7 +157,7 @@ inline bool wait_until(
|
||||
~child_cleaner_t()
|
||||
{
|
||||
int res;
|
||||
::kill(pid, -15);
|
||||
::kill(pid, SIGKILL);
|
||||
::waitpid(pid, &res, WNOHANG);
|
||||
}
|
||||
};
|
||||
@@ -133,19 +165,21 @@ inline bool wait_until(
|
||||
|
||||
do
|
||||
{
|
||||
int ret_sig = 0;
|
||||
int sig_{0};
|
||||
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
|
||||
&& (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
ret_sig = ::sigwait(&sigset, nullptr);
|
||||
&& (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
|
||||
return false;
|
||||
|
||||
ret = ::sigwait(&sigset, &sig_);
|
||||
errno = 0;
|
||||
|
||||
ret = ::waitpid(p.pid, &status, WNOHANG);
|
||||
|
||||
if ((ret_sig == SIGCHLD) &&
|
||||
if ((sig_ == SIGCHLD) &&
|
||||
(old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
|
||||
old_sig.sa_handler(ret);
|
||||
|
||||
if (ret <= 0)
|
||||
ret = ::waitpid(p.pid, &status, WNOHANG);
|
||||
if (ret == 0) // == > is running
|
||||
{
|
||||
timed_out = Clock::now() >= time_out;
|
||||
if (timed_out)
|
||||
|
||||
@@ -59,119 +59,33 @@ inline bool wait_until(
|
||||
std::error_code & ec) noexcept
|
||||
{
|
||||
|
||||
::sigset_t sigset;
|
||||
::siginfo_t siginfo;
|
||||
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGCHLD);
|
||||
|
||||
|
||||
auto get_timespec =
|
||||
[](const Duration & dur)
|
||||
{
|
||||
::timespec ts;
|
||||
ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
|
||||
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() % 1000000000;
|
||||
return ts;
|
||||
};
|
||||
|
||||
|
||||
bool timed_out = false;
|
||||
int ret;
|
||||
|
||||
struct ::sigaction old_sig;
|
||||
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
|
||||
{
|
||||
ec = get_last_error();
|
||||
return false;
|
||||
}
|
||||
::timespec sleep_interval;
|
||||
sleep_interval.tv_sec = 0;
|
||||
sleep_interval.tv_nsec = 100000000;
|
||||
|
||||
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
|
||||
do
|
||||
{
|
||||
auto ts = get_timespec(time_out - Clock::now());
|
||||
ret = ::sigtimedwait(&sigset, nullptr, &ts);
|
||||
errno = 0;
|
||||
if ((ret == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
|
||||
old_sig.sa_handler(ret);
|
||||
|
||||
ret = ::waitpid(-p.grp, &siginfo.si_status, 0); //so in case it exited, we wanna reap it first
|
||||
while (!(timed_out = (Clock::now() > time_out)))
|
||||
{
|
||||
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED | WNOHANG);
|
||||
if (ret == -1)
|
||||
{
|
||||
ec = get_last_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
//check if we're done
|
||||
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
|
||||
|
||||
}
|
||||
while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out)));
|
||||
#else
|
||||
//if we do not have sigtimedwait, we fork off a child process to get the signal in time
|
||||
pid_t timeout_pid = ::fork();
|
||||
if (timeout_pid == -1)
|
||||
{
|
||||
ec = boost::process::detail::get_last_error();
|
||||
return true;
|
||||
}
|
||||
else if (timeout_pid == 0)
|
||||
{
|
||||
auto ts = get_timespec(time_out - Clock::now());
|
||||
::setpgid(0, p.grp);
|
||||
::nanosleep(&ts, nullptr);
|
||||
::exit(0);
|
||||
}
|
||||
|
||||
struct child_cleaner_t
|
||||
{
|
||||
pid_t pid;
|
||||
~child_cleaner_t()
|
||||
{
|
||||
int res;
|
||||
::kill(pid, -15);
|
||||
::waitpid(pid, &res, WNOHANG);
|
||||
}
|
||||
};
|
||||
child_cleaner_t child_cleaner{timeout_pid};
|
||||
|
||||
do
|
||||
{
|
||||
int ret_sig = 0;
|
||||
int status;
|
||||
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
|
||||
&& (WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
ret = ::sigwait(&sigset, nullptr);
|
||||
errno = 0;
|
||||
if ((ret == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
|
||||
old_sig.sa_handler(ret);
|
||||
|
||||
ret = ::waitpid(-p.grp, &siginfo.si_status, 0); //so in case it exited, we wanna reap it first
|
||||
if (ret == -1)
|
||||
{
|
||||
ec = get_last_error();
|
||||
if ((errno == ECHILD) || (errno == ESRCH))
|
||||
{
|
||||
ec.clear();
|
||||
return true;
|
||||
}
|
||||
ec = boost::process::detail::get_last_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
//check if we're done
|
||||
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
|
||||
|
||||
//we can wait, because unlike in the wait_for_exit, we have no race condition regarding eh exit code.
|
||||
::nanosleep(&sleep_interval, nullptr);
|
||||
}
|
||||
while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out)));
|
||||
|
||||
#endif
|
||||
|
||||
if (errno != ECHILD)
|
||||
{
|
||||
ec = boost::process::detail::get_last_error();
|
||||
return !timed_out;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.clear();
|
||||
return true; //even if timed out, there are no child proccessess left
|
||||
}
|
||||
|
||||
return !timed_out;
|
||||
}
|
||||
|
||||
template< class Clock, class Duration >
|
||||
|
||||
81
include/boost/process/detail/used_handles.hpp
Normal file
81
include/boost/process/detail/used_handles.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2016 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
#include <boost/fusion/include/filter_if.hpp>
|
||||
#include <boost/fusion/include/for_each.hpp>
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
#include <boost/process/detail/posix/handles.hpp>
|
||||
#include <boost/process/detail/posix/asio_fwd.hpp>
|
||||
#else
|
||||
#include <boost/process/detail/windows/handles.hpp>
|
||||
#include <boost/process/detail/windows/asio_fwd.hpp>
|
||||
#endif
|
||||
|
||||
namespace boost { namespace process { namespace detail {
|
||||
|
||||
struct uses_handles
|
||||
{
|
||||
//If you get an error here, you must add a `get_handles` function that returns a range or a single handle value
|
||||
void get_used_handles() const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct does_use_handle: std::is_base_of<uses_handles, T> {};
|
||||
|
||||
template<typename T>
|
||||
struct does_use_handle<T&> : std::is_base_of<uses_handles, T> {};
|
||||
|
||||
template<typename T>
|
||||
struct does_use_handle<const T&> : std::is_base_of<uses_handles, T> {};
|
||||
|
||||
template<typename Char, typename Sequence>
|
||||
class executor;
|
||||
|
||||
template<typename Func>
|
||||
struct foreach_handle_invocator
|
||||
{
|
||||
Func & func;
|
||||
foreach_handle_invocator(Func & func) : func(func) {}
|
||||
|
||||
|
||||
template<typename Range>
|
||||
void invoke(const Range & range) const
|
||||
{
|
||||
for (auto handle_ : range)
|
||||
func(handle_);
|
||||
|
||||
}
|
||||
void invoke(::boost::process::detail::api::native_handle_type handle) const {func(handle);};
|
||||
|
||||
template<typename T>
|
||||
void operator()(T & val) const {invoke(val.get_used_handles());}
|
||||
};
|
||||
|
||||
template<typename Executor, typename Function>
|
||||
void foreach_used_handle(Executor &exec, Function &&func)
|
||||
{
|
||||
boost::fusion::for_each(boost::fusion::filter_if<does_use_handle<boost::mpl::_>>(exec.seq),
|
||||
foreach_handle_invocator<Function>(func));
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
std::vector<::boost::process::detail::api::native_handle_type>
|
||||
get_used_handles(Executor &exec)
|
||||
{
|
||||
std::vector<::boost::process::detail::api::native_handle_type> res;
|
||||
foreach_used_handle(exec, [&](::boost::process::detail::api::native_handle_type handle){res.push_back(handle);});
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}}}
|
||||
|
||||
#endif /* BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_ */
|
||||
@@ -7,6 +7,7 @@
|
||||
#define BOOST_PROCESS_DETAIL_WINDOWS_ASIO_FWD_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <boost/asio/ts/netfwd.hpp>
|
||||
|
||||
namespace boost { namespace asio {
|
||||
|
||||
@@ -19,43 +20,19 @@ template<typename Allocator>
|
||||
class basic_streambuf;
|
||||
|
||||
typedef basic_streambuf<std::allocator<char>> streambuf;
|
||||
class io_context;
|
||||
|
||||
class executor;
|
||||
|
||||
|
||||
template <typename Handler>
|
||||
class basic_yield_context;
|
||||
|
||||
namespace windows {
|
||||
|
||||
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
|
||||
class stream_handle_service;
|
||||
|
||||
template <typename StreamHandleService>
|
||||
class basic_stream_handle;
|
||||
|
||||
typedef basic_stream_handle<stream_handle_service> stream_handle;
|
||||
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
template <typename Executor>
|
||||
class basic_stream_handle;
|
||||
typedef basic_stream_handle<executor> stream_handle;
|
||||
typedef basic_stream_handle<any_io_executor> stream_handle;
|
||||
|
||||
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
|
||||
|
||||
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
|
||||
class object_handle_service;
|
||||
|
||||
template <typename ObjectHandleService>
|
||||
class basic_object_handle;
|
||||
|
||||
typedef basic_object_handle<object_handle_service> object_handle;
|
||||
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
template <typename Executor>
|
||||
class basic_object_handle;
|
||||
typedef basic_object_handle<executor> object_handle;
|
||||
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
|
||||
typedef basic_object_handle<any_io_executor> object_handle;
|
||||
|
||||
} //windows
|
||||
} //asio
|
||||
|
||||
@@ -17,19 +17,20 @@
|
||||
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/async_handler.hpp>
|
||||
#include <boost/process/detail/windows/asio_fwd.hpp>
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
|
||||
template<typename Buffer>
|
||||
struct async_in_buffer : ::boost::process::detail::windows::handler_base_ext,
|
||||
::boost::process::detail::windows::require_io_context
|
||||
::boost::process::detail::windows::require_io_context,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
Buffer & buf;
|
||||
|
||||
@@ -42,6 +43,11 @@ struct async_in_buffer : ::boost::process::detail::windows::handler_base_ext,
|
||||
|
||||
std::shared_ptr<boost::process::async_pipe> pipe;
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const
|
||||
{
|
||||
return std::move(*pipe).source().native_handle();
|
||||
}
|
||||
|
||||
async_in_buffer(Buffer & buf) : buf(buf)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <boost/winapi/error_codes.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/asio_fwd.hpp>
|
||||
|
||||
#include <istream>
|
||||
@@ -108,12 +109,18 @@ struct async_out_buffer : ::boost::process::detail::windows::handler_base_ext,
|
||||
|
||||
template<int p1, int p2, typename Type>
|
||||
struct async_out_future : ::boost::process::detail::windows::handler_base_ext,
|
||||
::boost::process::detail::windows::require_io_context
|
||||
::boost::process::detail::windows::require_io_context,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
std::shared_ptr<boost::process::async_pipe> pipe;
|
||||
std::shared_ptr<std::promise<Type>> promise = std::make_shared<std::promise<Type>>();
|
||||
std::shared_ptr<boost::asio::streambuf> buffer = std::make_shared<boost::asio::streambuf>();
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const
|
||||
{
|
||||
return std::move(*pipe).sink().native_handle();
|
||||
}
|
||||
|
||||
|
||||
async_out_future(std::future<Type> & fut)
|
||||
{
|
||||
|
||||
@@ -40,23 +40,31 @@ class async_pipe
|
||||
{
|
||||
::boost::asio::windows::stream_handle _source;
|
||||
::boost::asio::windows::stream_handle _sink ;
|
||||
public:
|
||||
typedef ::boost::winapi::HANDLE_ native_handle_type;
|
||||
typedef ::boost::asio::windows::stream_handle handle_type;
|
||||
|
||||
inline async_pipe(boost::asio::io_context & ios,
|
||||
const std::string & name = make_pipe_name())
|
||||
: async_pipe(ios, ios, name) {}
|
||||
|
||||
inline async_pipe(boost::asio::io_context & ios_source,
|
||||
boost::asio::io_context & ios_sink,
|
||||
const std::string & name = make_pipe_name());
|
||||
const std::string & name, bool private_);
|
||||
|
||||
public:
|
||||
typedef ::boost::winapi::HANDLE_ native_handle_type;
|
||||
typedef ::boost::asio::windows::stream_handle handle_type;
|
||||
typedef typename handle_type::executor_type executor_type;
|
||||
|
||||
async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios, make_pipe_name(), true) {}
|
||||
async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink)
|
||||
: async_pipe(ios_source, ios_sink, make_pipe_name(), true) {}
|
||||
|
||||
async_pipe(boost::asio::io_context & ios, const std::string & name)
|
||||
: async_pipe(ios, ios, name, false) {}
|
||||
|
||||
async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink, const std::string & name)
|
||||
: async_pipe(ios_source, ios_sink, name, false) {}
|
||||
|
||||
|
||||
|
||||
inline async_pipe(const async_pipe& rhs);
|
||||
async_pipe(async_pipe&& rhs) : _source(std::move(rhs._source)), _sink(std::move(rhs._sink))
|
||||
{
|
||||
rhs._source.assign (::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
rhs._sink .assign (::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
}
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
explicit async_pipe(::boost::asio::io_context & ios_source,
|
||||
@@ -191,13 +199,15 @@ public:
|
||||
handle_type source(::boost::asio::io_context& ios) &&
|
||||
{
|
||||
::boost::asio::windows::stream_handle stolen(ios.get_executor(), _source.native_handle());
|
||||
_source.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
boost::system::error_code ec;
|
||||
_source.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
|
||||
return stolen;
|
||||
}
|
||||
handle_type sink (::boost::asio::io_context& ios) &&
|
||||
{
|
||||
::boost::asio::windows::stream_handle stolen(ios.get_executor(), _sink.native_handle());
|
||||
_sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
boost::system::error_code ec;
|
||||
_sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
|
||||
return stolen;
|
||||
}
|
||||
|
||||
@@ -235,12 +245,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
async_pipe::async_pipe(const async_pipe& p) :
|
||||
_source(const_cast<handle_type&>(p._source).get_executor()),
|
||||
_sink (const_cast<handle_type&>(p._sink).get_executor())
|
||||
{
|
||||
|
||||
auto proc = ::boost::winapi::GetCurrentProcess();
|
||||
|
||||
::boost::winapi::HANDLE_ source;
|
||||
@@ -266,14 +275,16 @@ async_pipe::async_pipe(const async_pipe& p) :
|
||||
::boost::winapi::DUPLICATE_SAME_ACCESS_))
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
_source.assign(source);
|
||||
_sink. assign(sink);
|
||||
if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_source.assign(source);
|
||||
if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_sink. assign(sink);
|
||||
}
|
||||
|
||||
|
||||
async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
boost::asio::io_context & ios_sink,
|
||||
const std::string & name) : _source(ios_source), _sink(ios_sink)
|
||||
const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink)
|
||||
{
|
||||
static constexpr int FILE_FLAG_OVERLAPPED_ = 0x40000000; //temporary
|
||||
|
||||
@@ -285,7 +296,7 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
#endif
|
||||
::boost::winapi::PIPE_ACCESS_INBOUND_
|
||||
| FILE_FLAG_OVERLAPPED_, //write flag
|
||||
0, 1, 8192, 8192, 0, nullptr);
|
||||
0, private_ ? 1 : ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
|
||||
|
||||
|
||||
if (source == boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
@@ -310,6 +321,44 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
_sink.assign(sink);
|
||||
}
|
||||
|
||||
template<class CharT, class Traits>
|
||||
async_pipe& async_pipe::operator=(const basic_pipe<CharT, Traits> & p)
|
||||
{
|
||||
auto proc = ::boost::winapi::GetCurrentProcess();
|
||||
|
||||
::boost::winapi::HANDLE_ source;
|
||||
::boost::winapi::HANDLE_ sink;
|
||||
|
||||
//cannot get the handle from a const object.
|
||||
auto source_in = p.native_source();
|
||||
auto sink_in = p.native_sink();
|
||||
|
||||
if (source_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
source = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
else if (!::boost::winapi::DuplicateHandle(
|
||||
proc, source_in.native_handle(), proc, &source, 0,
|
||||
static_cast<::boost::winapi::BOOL_>(true),
|
||||
::boost::winapi::DUPLICATE_SAME_ACCESS_))
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
if (sink_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
sink = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
else if (!::boost::winapi::DuplicateHandle(
|
||||
proc, sink_in.native_handle(), proc, &sink, 0,
|
||||
static_cast<::boost::winapi::BOOL_>(true),
|
||||
::boost::winapi::DUPLICATE_SAME_ACCESS_))
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
//so we also assign the io_context
|
||||
if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_source.assign(source);
|
||||
|
||||
if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_sink.assign(sink);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
async_pipe& async_pipe::operator=(const async_pipe & p)
|
||||
{
|
||||
auto proc = ::boost::winapi::GetCurrentProcess();
|
||||
@@ -321,6 +370,8 @@ async_pipe& async_pipe::operator=(const async_pipe & p)
|
||||
auto &source_in = const_cast<::boost::asio::windows::stream_handle &>(p._source);
|
||||
auto &sink_in = const_cast<::boost::asio::windows::stream_handle &>(p._sink);
|
||||
|
||||
source_in.get_executor();
|
||||
|
||||
if (source_in.native_handle() == ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
source = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
else if (!::boost::winapi::DuplicateHandle(
|
||||
@@ -338,24 +389,23 @@ async_pipe& async_pipe::operator=(const async_pipe & p)
|
||||
throw_last_error("Duplicate Pipe Failed");
|
||||
|
||||
//so we also assign the io_context
|
||||
_source = ::boost::asio::windows::stream_handle(source_in.get_executor(), source);
|
||||
_sink = ::boost::asio::windows::stream_handle(source_in.get_executor(), sink);
|
||||
if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_source = ::boost::asio::windows::stream_handle(source_in.get_executor(), source);
|
||||
else
|
||||
_source = ::boost::asio::windows::stream_handle(source_in.get_executor());
|
||||
|
||||
if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
_sink = ::boost::asio::windows::stream_handle(source_in.get_executor(), sink);
|
||||
else
|
||||
_sink = ::boost::asio::windows::stream_handle(source_in.get_executor());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
async_pipe& async_pipe::operator=(async_pipe && rhs)
|
||||
{
|
||||
if (_source.native_handle() != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
::boost::winapi::CloseHandle(_source.native_handle());
|
||||
|
||||
if (_sink.native_handle() != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
::boost::winapi::CloseHandle(_sink.native_handle());
|
||||
|
||||
_source.assign(rhs._source.native_handle());
|
||||
_sink .assign(rhs._sink .native_handle());
|
||||
rhs._source.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
rhs._sink .assign(::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
_source = std::move(rhs._source);
|
||||
_sink = std::move(rhs._sink);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,14 +30,17 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
|
||||
{
|
||||
std::string st = exe;
|
||||
|
||||
//put in quotes if it has spaces
|
||||
//put in quotes if it has spaces or double quotes
|
||||
if(!exe.empty())
|
||||
{
|
||||
boost::replace_all(st, "\"", "\\\"");
|
||||
auto it = st.find_first_of(" \"");
|
||||
|
||||
auto it = std::find(st.begin(), st.end(), ' ');
|
||||
|
||||
if (it != st.end())//contains spaces.
|
||||
if(it != st.npos)//contains spaces.
|
||||
{
|
||||
// double existing quotes
|
||||
boost::replace_all(st, "\"", "\"\"");
|
||||
|
||||
// surround with quotes
|
||||
st.insert(st.begin(), '"');
|
||||
st += '"';
|
||||
}
|
||||
@@ -45,15 +48,18 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
|
||||
|
||||
for (auto & arg : data)
|
||||
{
|
||||
boost::replace_all(arg, "\"", "\\\"");
|
||||
|
||||
auto it = std::find(arg.begin(), arg.end(), ' ');//contains space?
|
||||
if (it != arg.end())//ok, contains spaces.
|
||||
if(!arg.empty())
|
||||
{
|
||||
//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 += '"'; //thats the post one.
|
||||
auto it = arg.find_first_of(" \"");//contains space or double quotes?
|
||||
if(it != arg.npos)//yes
|
||||
{
|
||||
// double existing quotes
|
||||
boost::replace_all(arg, "\"", "\"\"");
|
||||
|
||||
// surround with quotes
|
||||
arg.insert(arg.begin(), '"');
|
||||
arg += '"';
|
||||
}
|
||||
}
|
||||
|
||||
if (!st.empty())//first one does not need a preceeding space
|
||||
@@ -68,30 +74,36 @@ inline std::wstring build_args(const std::wstring & exe, std::vector<std::wstrin
|
||||
{
|
||||
std::wstring st = exe;
|
||||
|
||||
//put in quotes if it has spaces
|
||||
//put in quotes if it has spaces or double quotes
|
||||
if(!exe.empty())
|
||||
{
|
||||
boost::replace_all(st, L"\"", L"\\\"");
|
||||
auto it = st.find_first_of(L" \"");
|
||||
|
||||
auto it = std::find(st.begin(), st.end(), L' ');
|
||||
|
||||
if (it != st.end())//contains spaces.
|
||||
if(it != st.npos)//contains spaces or double quotes.
|
||||
{
|
||||
// double existing quotes
|
||||
boost::replace_all(st, L"\"", L"\"\"");
|
||||
|
||||
// surround with quotes
|
||||
st.insert(st.begin(), L'"');
|
||||
st += L'"';
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & arg : data)
|
||||
for(auto & arg : data)
|
||||
{
|
||||
boost::replace_all(arg, L"\"", L"\\\"");
|
||||
|
||||
auto it = std::find(arg.begin(), arg.end(), L' ');//contains space?
|
||||
if (it != arg.end())//ok, contains spaces.
|
||||
if(!arg.empty())
|
||||
{
|
||||
//the first one is put directly onto the output,
|
||||
//because then I don't have to copy the whole string
|
||||
arg.insert(arg.begin(), L'"');
|
||||
arg += L'"'; //thats the post one.
|
||||
auto it = arg.find_first_of(L" \"");//contains space or double quotes?
|
||||
if(it != arg.npos)//yes
|
||||
{
|
||||
// double existing quotes
|
||||
boost::replace_all(arg, L"\"", L"\"\"");
|
||||
|
||||
// surround with quotes
|
||||
arg.insert(arg.begin(), L'"');
|
||||
arg += '"';
|
||||
}
|
||||
}
|
||||
|
||||
if (!st.empty())//first one does not need a preceeding space
|
||||
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
return static_cast<int_type>(read_len);
|
||||
}
|
||||
|
||||
bool is_open()
|
||||
bool is_open() const
|
||||
{
|
||||
return (_source != ::boost::winapi::INVALID_HANDLE_VALUE_) ||
|
||||
(_sink != ::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
@@ -152,7 +152,7 @@ basic_pipe<Char, Traits>::basic_pipe(const std::string & name)
|
||||
name_.c_str(),
|
||||
::boost::winapi::PIPE_ACCESS_INBOUND_
|
||||
| FILE_FLAG_OVERLAPPED_, //write flag
|
||||
0, 1, 8192, 8192, 0, nullptr);
|
||||
0, ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
|
||||
|
||||
if (source == boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
::boost::process::detail::throw_last_error("create_named_pipe() failed");
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
typedef int pid_t;
|
||||
typedef ::boost::winapi::DWORD_ pid_t;
|
||||
|
||||
struct child_handle
|
||||
{
|
||||
@@ -66,7 +66,7 @@ struct child_handle
|
||||
|
||||
pid_t id() const
|
||||
{
|
||||
return static_cast<int>(proc_info.dwProcessId);
|
||||
return static_cast<pid_t>(proc_info.dwProcessId);
|
||||
}
|
||||
|
||||
typedef ::boost::winapi::HANDLE_ process_handle_t;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <boost/winapi/file_management.hpp>
|
||||
#include <string>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
@@ -90,10 +91,18 @@ struct file_descriptor
|
||||
|
||||
}
|
||||
file_descriptor(const file_descriptor & ) = delete;
|
||||
file_descriptor(file_descriptor && ) = default;
|
||||
file_descriptor(file_descriptor &&other)
|
||||
: _handle( boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_) )
|
||||
{
|
||||
}
|
||||
|
||||
file_descriptor& operator=(const file_descriptor & ) = delete;
|
||||
file_descriptor& operator=(file_descriptor && ) = default;
|
||||
file_descriptor& operator=(file_descriptor &&other)
|
||||
{
|
||||
if (_handle != ::boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
::boost::winapi::CloseHandle(_handle);
|
||||
_handle = boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
}
|
||||
|
||||
~file_descriptor()
|
||||
{
|
||||
|
||||
@@ -13,16 +13,20 @@
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/file_descriptor.hpp>
|
||||
#include <io.h>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
struct file_in : public ::boost::process::detail::handler_base
|
||||
struct file_in : public ::boost::process::detail::handler_base,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor file;
|
||||
::boost::winapi::HANDLE_ handle = file.handle();
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
template<typename T>
|
||||
file_in(T&& t) : file(std::forward<T>(t), file_descriptor::read) {}
|
||||
file_in(FILE * f) : handle(reinterpret_cast<::boost::winapi::HANDLE_>(_get_osfhandle(_fileno(f)))) {}
|
||||
|
||||
@@ -14,16 +14,21 @@
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/winapi/handle_info.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/file_descriptor.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct file_out : public ::boost::process::detail::handler_base
|
||||
struct file_out : public ::boost::process::detail::handler_base,
|
||||
::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor file;
|
||||
::boost::winapi::HANDLE_ handle = file.handle();
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
|
||||
template<typename T>
|
||||
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write) {}
|
||||
file_out(FILE * f) : handle(reinterpret_cast<void*>(_get_osfhandle(_fileno(f)))) {}
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_GROUP_REF_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_WINDOWS_GROUP_REF_HPP_
|
||||
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/process/detail/config.hpp>
|
||||
#include <boost/process/detail/windows/group_handle.hpp>
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/handler.hpp>
|
||||
|
||||
namespace boost { namespace process {
|
||||
@@ -17,10 +18,12 @@ namespace detail { namespace windows {
|
||||
|
||||
|
||||
|
||||
struct group_ref : handler_base_ext
|
||||
struct group_ref : handler_base_ext, ::boost::process::detail::uses_handles
|
||||
{
|
||||
::boost::winapi::HANDLE_ handle;
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
explicit group_ref(group_handle &g) :
|
||||
handle(g.handle())
|
||||
{}
|
||||
|
||||
262
include/boost/process/detail/windows/handle_workaround.hpp
Normal file
262
include/boost/process/detail/windows/handle_workaround.hpp
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright (c) 2018 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLE_WORKAROUND_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_WINDOWS_HANDLE_WORKAROUND_HPP_
|
||||
|
||||
#include <boost/winapi/basic_types.hpp>
|
||||
#include <boost/winapi/dll.hpp>
|
||||
#include <boost/winapi/access_rights.hpp>
|
||||
//#define BOOST_USE_WINDOWS_H 1
|
||||
|
||||
#if defined( BOOST_USE_WINDOWS_H )
|
||||
#include <Winternl.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows { namespace workaround
|
||||
{
|
||||
|
||||
|
||||
typedef struct _SYSTEM_HANDLE_ENTRY_
|
||||
{
|
||||
::boost::winapi::ULONG_ OwnerPid;
|
||||
::boost::winapi::BYTE_ ObjectType;
|
||||
::boost::winapi::BYTE_ HandleFlags;
|
||||
::boost::winapi::USHORT_ HandleValue;
|
||||
::boost::winapi::PVOID_ ObjectPointer;
|
||||
::boost::winapi::ULONG_ AccessMask;
|
||||
} SYSTEM_HANDLE_ENTRY_, *PSYSTEM_HANDLE_ENTRY_;
|
||||
|
||||
typedef struct _SYSTEM_HANDLE_INFORMATION_
|
||||
{
|
||||
::boost::winapi::ULONG_ Count;
|
||||
SYSTEM_HANDLE_ENTRY_ Handle[1];
|
||||
} SYSTEM_HANDLE_INFORMATION_, *PSYSTEM_HANDLE_INFORMATION_;
|
||||
|
||||
#if defined( BOOST_USE_WINDOWS_H )
|
||||
|
||||
using UNICODE_STRING_ = ::UNICODE_STRING;
|
||||
using GENERIC_MAPPING_ = ::GENERIC_MAPPING;
|
||||
using OBJECT_INFORMATION_CLASS_ = ::OBJECT_INFORMATION_CLASS;
|
||||
|
||||
constexpr static OBJECT_INFORMATION_CLASS_ ObjectTypeInformation = ::OBJECT_INFORMATION_CLASS::ObjectTypeInformation;
|
||||
|
||||
typedef struct _OBJECT_TYPE_INFORMATION_ {
|
||||
UNICODE_STRING TypeName;
|
||||
ULONG TotalNumberOfObjects;
|
||||
ULONG TotalNumberOfHandles;
|
||||
ULONG TotalPagedPoolUsage;
|
||||
ULONG TotalNonPagedPoolUsage;
|
||||
ULONG TotalNamePoolUsage;
|
||||
ULONG TotalHandleTableUsage;
|
||||
ULONG HighWaterNumberOfObjects;
|
||||
ULONG HighWaterNumberOfHandles;
|
||||
ULONG HighWaterPagedPoolUsage;
|
||||
ULONG HighWaterNonPagedPoolUsage;
|
||||
ULONG HighWaterNamePoolUsage;
|
||||
ULONG HighWaterHandleTableUsage;
|
||||
ULONG InvalidAttributes;
|
||||
GENERIC_MAPPING GenericMapping;
|
||||
ULONG ValidAccessMask;
|
||||
BOOLEAN SecurityRequired;
|
||||
BOOLEAN MaintainHandleCount;
|
||||
UCHAR TypeIndex;
|
||||
CHAR ReservedByte;
|
||||
ULONG PoolType;
|
||||
ULONG DefaultPagedPoolCharge;
|
||||
ULONG DefaultNonPagedPoolCharge;
|
||||
} OBJECT_TYPE_INFORMATION_, *POBJECT_TYPE_INFORMATION_;
|
||||
|
||||
#else
|
||||
|
||||
typedef enum _OBJECT_INFORMATION_CLASS_
|
||||
{
|
||||
ObjectBasicInformation,
|
||||
ObjectNameInformation,
|
||||
ObjectTypeInformation,
|
||||
ObjectAllInformation,
|
||||
ObjectDataInformation
|
||||
} OBJECT_INFORMATION_CLASS_, *POBJECT_INFORMATION_CLASS_;
|
||||
|
||||
typedef struct _UNICODE_STRING_ {
|
||||
::boost::winapi::USHORT_ Length;
|
||||
::boost::winapi::USHORT_ MaximumLength;
|
||||
::boost::winapi::LPWSTR_ Buffer;
|
||||
} UNICODE_STRING_, *PUNICODE_STRING_;
|
||||
|
||||
typedef struct _GENERIC_MAPPING_ {
|
||||
::boost::winapi::ACCESS_MASK_ GenericRead;
|
||||
::boost::winapi::ACCESS_MASK_ GenericWrite;
|
||||
::boost::winapi::ACCESS_MASK_ GenericExecute;
|
||||
::boost::winapi::ACCESS_MASK_ GenericAll;
|
||||
} GENERIC_MAPPING_;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct _OBJECT_BASIC_INFORMATION {
|
||||
::boost::winapi::ULONG_ Attributes;
|
||||
::boost::winapi::ACCESS_MASK_ GrantedAccess;
|
||||
::boost::winapi::ULONG_ HandleCount;
|
||||
::boost::winapi::ULONG_ PointerCount;
|
||||
::boost::winapi::ULONG_ PagedPoolUsage;
|
||||
::boost::winapi::ULONG_ NonPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ Reserved[3];
|
||||
::boost::winapi::ULONG_ NameInformationLength;
|
||||
::boost::winapi::ULONG_ TypeInformationLength;
|
||||
::boost::winapi::ULONG_ SecurityDescriptorLength;
|
||||
::boost::winapi::LARGE_INTEGER_ CreateTime;
|
||||
} OBJECT_BASIC_INFORMATION_, *POBJECT_BASIC_INFORMATION_;
|
||||
|
||||
typedef struct _OBJECT_NAME_INFORMATION {
|
||||
UNICODE_STRING_ Name;
|
||||
} OBJECT_NAME_INFORMATION_, *POBJECT_NAME_INFORMATION_;
|
||||
|
||||
|
||||
#if defined( BOOST_USE_WINDOWS_H )
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
using SYSTEM_INFORMATION_CLASS_ = ::SYSTEM_INFORMATION_CLASS;
|
||||
constexpr static SYSTEM_INFORMATION_CLASS_ SystemHandleInformation_ = static_cast<SYSTEM_INFORMATION_CLASS_>(16);
|
||||
|
||||
inline ::boost::winapi::NTSTATUS_ nt_system_query_information(
|
||||
SYSTEM_INFORMATION_CLASS SystemInformationClass,
|
||||
void * SystemInformation,
|
||||
::boost::winapi::ULONG_ SystemInformationLength,
|
||||
::boost::winapi::PULONG_ ReturnLength)
|
||||
{
|
||||
return ::NtQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
|
||||
}
|
||||
|
||||
inline ::boost::winapi::NTSTATUS_ nt_query_object(
|
||||
::boost::winapi::HANDLE_ Handle,
|
||||
OBJECT_INFORMATION_CLASS_ ObjectInformationClass,
|
||||
::boost::winapi::PVOID_ ObjectInformation,
|
||||
::boost::winapi::ULONG_ ObjectInformationLength,
|
||||
::boost::winapi::PULONG_ ReturnLength
|
||||
)
|
||||
{
|
||||
return ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
//this import workaround is to keep it a header-only library. and enums cannot be imported from the winapi.
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
typedef enum _SYSTEM_INFORMATION_CLASS_
|
||||
{
|
||||
SystemBasicInformation_ = 0,
|
||||
SystemProcessorInformation_ = 1,
|
||||
SystemPerformanceInformation_ = 2,
|
||||
SystemTimeOfDayInformation_ = 3,
|
||||
SystemProcessInformation_ = 5,
|
||||
SystemProcessorPerformanceInformation_ = 8,
|
||||
SystemHandleInformation_ = 16,
|
||||
SystemPagefileInformation_ = 18,
|
||||
SystemInterruptInformation_ = 23,
|
||||
SystemExceptionInformation_ = 33,
|
||||
SystemRegistryQuotaInformation_ = 37,
|
||||
SystemLookasideInformation_ = 45
|
||||
} SYSTEM_INFORMATION_CLASS_;
|
||||
|
||||
|
||||
typedef struct _OBJECT_TYPE_INFORMATION_ {
|
||||
UNICODE_STRING_ TypeName;
|
||||
::boost::winapi::ULONG_ TotalNumberOfObjects;
|
||||
::boost::winapi::ULONG_ TotalNumberOfHandles;
|
||||
::boost::winapi::ULONG_ TotalPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ TotalNonPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ TotalNamePoolUsage;
|
||||
::boost::winapi::ULONG_ TotalHandleTableUsage;
|
||||
::boost::winapi::ULONG_ HighWaterNumberOfObjects;
|
||||
::boost::winapi::ULONG_ HighWaterNumberOfHandles;
|
||||
::boost::winapi::ULONG_ HighWaterPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ HighWaterNonPagedPoolUsage;
|
||||
::boost::winapi::ULONG_ HighWaterNamePoolUsage;
|
||||
::boost::winapi::ULONG_ HighWaterHandleTableUsage;
|
||||
::boost::winapi::ULONG_ InvalidAttributes;
|
||||
GENERIC_MAPPING_ GenericMapping;
|
||||
::boost::winapi::ULONG_ ValidAccessMask;
|
||||
::boost::winapi::BOOLEAN_ SecurityRequired;
|
||||
::boost::winapi::BOOLEAN_ MaintainHandleCount;
|
||||
::boost::winapi::UCHAR_ TypeIndex;
|
||||
::boost::winapi::CHAR_ ReservedByte;
|
||||
::boost::winapi::ULONG_ PoolType;
|
||||
::boost::winapi::ULONG_ DefaultPagedPoolCharge;
|
||||
::boost::winapi::ULONG_ DefaultNonPagedPoolCharge;
|
||||
} OBJECT_TYPE_INFORMATION_, *POBJECT_TYPE_INFORMATION_;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
__kernel_entry NTSTATUS NtQuerySystemInformation(
|
||||
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
|
||||
OUT PVOID SystemInformation,
|
||||
IN ULONG SystemInformationLength,
|
||||
OUT PULONG ReturnLength
|
||||
);
|
||||
*/
|
||||
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_system_query_information_p )(
|
||||
SYSTEM_INFORMATION_CLASS_,
|
||||
void *,
|
||||
::boost::winapi::ULONG_,
|
||||
::boost::winapi::PULONG_);
|
||||
/*
|
||||
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject(
|
||||
HANDLE Handle,
|
||||
OBJECT_INFORMATION_CLASS ObjectInformationClass,
|
||||
PVOID ObjectInformation,
|
||||
ULONG ObjectInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
*/
|
||||
|
||||
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_query_object_p )(
|
||||
::boost::winapi::HANDLE_,
|
||||
OBJECT_INFORMATION_CLASS_,
|
||||
void *,
|
||||
::boost::winapi::ULONG_,
|
||||
::boost::winapi::PULONG_);
|
||||
|
||||
}
|
||||
|
||||
inline ::boost::winapi::NTSTATUS_ nt_system_query_information(
|
||||
SYSTEM_INFORMATION_CLASS_ SystemInformationClass,
|
||||
void *SystemInformation,
|
||||
::boost::winapi::ULONG_ SystemInformationLength,
|
||||
::boost::winapi::PULONG_ ReturnLength)
|
||||
{
|
||||
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Ntdll.dll");
|
||||
static nt_system_query_information_p f = reinterpret_cast<nt_system_query_information_p>(::boost::winapi::get_proc_address(h, "NtQuerySystemInformation"));
|
||||
|
||||
return (*f)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
|
||||
}
|
||||
|
||||
|
||||
inline ::boost::winapi::BOOL_ nt_query_object(
|
||||
::boost::winapi::HANDLE_ Handle,
|
||||
OBJECT_INFORMATION_CLASS_ ObjectInformationClass,
|
||||
void *ObjectInformation,
|
||||
::boost::winapi::ULONG_ ObjectInformationLength,
|
||||
::boost::winapi::PULONG_ ReturnLength)
|
||||
{
|
||||
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Ntdll.dll");
|
||||
static nt_query_object_p f = reinterpret_cast<nt_query_object_p>(::boost::winapi::get_proc_address(h, "NtQueryObject"));
|
||||
|
||||
return (*f)(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}}}}}
|
||||
|
||||
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_JOB_WORKAROUND_HPP_ */
|
||||
176
include/boost/process/detail/windows/handles.hpp
Normal file
176
include/boost/process/detail/windows/handles.hpp
Normal file
@@ -0,0 +1,176 @@
|
||||
// Copyright (c) 2019 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
|
||||
#define BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
|
||||
|
||||
#include <vector>
|
||||
#include <system_error>
|
||||
#include <boost/process/detail/windows/handle_workaround.hpp>
|
||||
#include <boost/process/detail/windows/handler.hpp>
|
||||
#include <boost/winapi/get_current_process_id.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail {
|
||||
|
||||
|
||||
template<typename Executor, typename Function>
|
||||
void foreach_used_handle(Executor &exec, Function &&func);
|
||||
|
||||
|
||||
namespace windows {
|
||||
|
||||
|
||||
using native_handle_type = ::boost::winapi::HANDLE_ ;
|
||||
|
||||
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
|
||||
{
|
||||
auto pid = ::boost::winapi::GetCurrentProcessId();
|
||||
|
||||
std::vector<char> buffer(2048);
|
||||
constexpr static auto STATUS_INFO_LENGTH_MISMATCH_ = static_cast<::boost::winapi::NTSTATUS_>(0xC0000004l);
|
||||
auto info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
|
||||
|
||||
::boost::winapi::NTSTATUS_ nt_status = STATUS_INFO_LENGTH_MISMATCH_;
|
||||
|
||||
for (;
|
||||
nt_status == STATUS_INFO_LENGTH_MISMATCH_;
|
||||
nt_status = workaround::nt_system_query_information(
|
||||
workaround::SystemHandleInformation_,
|
||||
info_pointer, static_cast<::boost::winapi::ULONG_>(buffer.size()),
|
||||
nullptr))
|
||||
{
|
||||
buffer.resize(buffer.size() * 2);
|
||||
info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
|
||||
}
|
||||
|
||||
|
||||
if (nt_status < 0 || nt_status > 0x7FFFFFFF)
|
||||
{
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
return {};
|
||||
}
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
std::vector<native_handle_type> res;
|
||||
for (auto itr = info_pointer->Handle; itr != (info_pointer->Handle + info_pointer->Count); itr++)
|
||||
{
|
||||
if (itr->OwnerPid == pid)
|
||||
res.push_back(reinterpret_cast<native_handle_type>(static_cast<std::uintptr_t>(itr->HandleValue)));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
inline std::vector<native_handle_type> get_handles()
|
||||
{
|
||||
std::error_code ec;
|
||||
|
||||
auto res = get_handles(ec);
|
||||
if (ec)
|
||||
boost::process::detail::throw_error(ec, "NtQuerySystemInformation failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
|
||||
{
|
||||
::boost::winapi::ULONG_ actual_size;
|
||||
auto nt_status = workaround::nt_query_object(
|
||||
handle,
|
||||
workaround::ObjectTypeInformation,
|
||||
NULL,
|
||||
0, &actual_size);
|
||||
|
||||
std::vector<char> vec;
|
||||
vec.resize(actual_size);
|
||||
|
||||
workaround::OBJECT_TYPE_INFORMATION_ * type_info_p = reinterpret_cast<workaround::OBJECT_TYPE_INFORMATION_*>(vec.data());
|
||||
nt_status = workaround::nt_query_object(
|
||||
handle,
|
||||
workaround::ObjectTypeInformation,
|
||||
type_info_p,
|
||||
actual_size, &actual_size);
|
||||
|
||||
if (nt_status < 0 || nt_status > 0x7FFFFFFF)
|
||||
{
|
||||
ec = ::boost::process::detail::get_last_error();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
auto &nm = type_info_p->TypeName.Buffer;
|
||||
return type_info_p->TypeName.Length >= 5 &&
|
||||
nm[0] == L'F' &&
|
||||
nm[1] == L'i' &&
|
||||
nm[2] == L'l' &&
|
||||
nm[3] == L'e' &&
|
||||
nm[4] == L'\0';
|
||||
}
|
||||
|
||||
|
||||
inline bool is_stream_handle(native_handle_type handle)
|
||||
{
|
||||
std::error_code ec;
|
||||
auto res = is_stream_handle(handle, ec);
|
||||
if (ec)
|
||||
boost::process::detail::throw_error(ec, "NtQueryObject failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
struct limit_handles_ : handler_base_ext
|
||||
{
|
||||
mutable std::vector<::boost::winapi::HANDLE_> handles_with_inherit_flag;
|
||||
|
||||
template<typename Executor>
|
||||
void on_setup(Executor & exec) const
|
||||
{
|
||||
auto all_handles = get_handles();
|
||||
foreach_used_handle(exec,
|
||||
[&](::boost::winapi::HANDLE_ handle)
|
||||
{
|
||||
auto itr = std::find(all_handles.begin(), all_handles .end(), handle);
|
||||
DWORD flags = 0u;
|
||||
if (itr != all_handles.end())
|
||||
*itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
else if ((::boost::winapi::GetHandleInformation(*itr, &flags) != 0)
|
||||
&&((flags & ::boost::winapi::HANDLE_FLAG_INHERIT_) == 0)) //it is NOT inherited anyhow, so ignore too
|
||||
*itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
|
||||
});
|
||||
|
||||
auto part_itr = std::partition(all_handles.begin(), all_handles.end(),
|
||||
[](::boost::winapi::HANDLE_ handle) {return handle != ::boost::winapi::INVALID_HANDLE_VALUE_;});
|
||||
|
||||
all_handles.erase(part_itr, all_handles.end()); //remove invalid handles
|
||||
handles_with_inherit_flag = std::move(all_handles);
|
||||
|
||||
for (auto handle : handles_with_inherit_flag)
|
||||
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, 0);
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
void on_error(Executor & exec, const std::error_code & ec) const
|
||||
{
|
||||
for (auto handle : handles_with_inherit_flag)
|
||||
::boost::winapi::SetHandleInformation(handle, ::boost::winapi::HANDLE_FLAG_INHERIT_, ::boost::winapi::HANDLE_FLAG_INHERIT_);
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
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_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}}}}
|
||||
|
||||
#endif //PROCESS_HANDLES_HPP
|
||||
@@ -14,14 +14,18 @@
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/winapi/handle_info.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/file_descriptor.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
struct null_in : public ::boost::process::detail::handler_base
|
||||
struct null_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor source{"NUL", file_descriptor::read};
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return source.handle(); }
|
||||
|
||||
|
||||
public:
|
||||
template <class WindowsExecutor>
|
||||
void on_setup(WindowsExecutor &e) const
|
||||
|
||||
@@ -14,15 +14,18 @@
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/winapi/handle_info.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/windows/file_descriptor.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
template<int p1, int p2>
|
||||
struct null_out : public ::boost::process::detail::handler_base
|
||||
struct null_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
file_descriptor sink {"NUL", file_descriptor::write}; //works because it gets destroyed AFTER launch.
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return sink.handle(); }
|
||||
|
||||
template <typename WindowsExecutor>
|
||||
void on_setup(WindowsExecutor &e) const;
|
||||
};
|
||||
|
||||
@@ -12,14 +12,17 @@
|
||||
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
struct pipe_in : public ::boost::process::detail::handler_base
|
||||
struct pipe_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
::boost::winapi::HANDLE_ handle;
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
pipe_in(::boost::winapi::HANDLE_ handle) : handle(handle) {}
|
||||
|
||||
template<typename T> //async_pipe
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <boost/winapi/process.hpp>
|
||||
#include <boost/winapi/handles.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
#include <boost/process/detail/handler_base.hpp>
|
||||
|
||||
namespace boost { namespace process { namespace detail { namespace windows {
|
||||
@@ -20,10 +21,12 @@ namespace boost { namespace process { namespace detail { namespace windows {
|
||||
|
||||
|
||||
template<int p1, int p2>
|
||||
struct pipe_out : public ::boost::process::detail::handler_base
|
||||
struct pipe_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
|
||||
{
|
||||
::boost::winapi::HANDLE_ handle;
|
||||
|
||||
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
|
||||
|
||||
pipe_out(::boost::winapi::HANDLE_ handle) : handle(handle) {}
|
||||
template<typename T>
|
||||
pipe_out(T & p) : handle(p.native_sink())
|
||||
|
||||
@@ -60,14 +60,14 @@ inline boost::filesystem::path search_path(
|
||||
auto p = pp_ / filename;
|
||||
for (boost::filesystem::path ext : extensions)
|
||||
{
|
||||
boost::filesystem::path pp = p;
|
||||
pp += ext;
|
||||
boost::filesystem::path pp_ext = p;
|
||||
pp_ext += ext;
|
||||
boost::system::error_code ec;
|
||||
bool file = boost::filesystem::is_regular_file(pp, ec);
|
||||
bool file = boost::filesystem::is_regular_file(pp_ext, ec);
|
||||
if (!ec && file &&
|
||||
::boost::winapi::sh_get_file_info(pp.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))
|
||||
::boost::winapi::sh_get_file_info(pp_ext.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))
|
||||
{
|
||||
return pp;
|
||||
return pp_ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ inline bool wait_impl(const group_handle & p, std::error_code & ec, std::chrono:
|
||||
|
||||
while (workaround::get_queued_completion_status(
|
||||
p._io_port, &completion_code,
|
||||
&completion_key, &overlapped, wait_time))
|
||||
&completion_key, &overlapped, static_cast<::boost::winapi::DWORD_>(wait_time)))
|
||||
{
|
||||
if (reinterpret_cast<::boost::winapi::HANDLE_>(completion_key) == p._job_object &&
|
||||
completion_code == workaround::JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO_)
|
||||
@@ -48,11 +48,11 @@ inline bool wait_impl(const group_handle & p, std::error_code & ec, std::chrono:
|
||||
return false; //correct, nothing left.
|
||||
}
|
||||
//reduce the remaining wait time -> in case interrupted by something else
|
||||
if (wait_time != ::boost::winapi::infinite)
|
||||
if (wait_time != static_cast<int>(::boost::winapi::infinite))
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
|
||||
wait_time -= diff.count();
|
||||
wait_time -= static_cast<std::chrono::system_clock::rep>(diff.count());
|
||||
start_time = now;
|
||||
if (wait_time <= 0)
|
||||
return true; //timeout with other source
|
||||
|
||||
@@ -44,7 +44,7 @@ struct const_entry
|
||||
bool operator()(char c) const {return c == api::env_seperator<char> ();}
|
||||
} s;
|
||||
boost::split(data, _data, s);
|
||||
return std::move(data);
|
||||
return data;
|
||||
}
|
||||
string_type to_string() const
|
||||
{
|
||||
@@ -677,11 +677,11 @@ inline std::vector<boost::filesystem::path> path()
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
const ::boost::process::wnative_environment ne{};
|
||||
typedef typename ::boost::process::wnative_environment::const_entry_type value_type;
|
||||
const auto id = L"PATH";
|
||||
static constexpr auto id = L"PATH";
|
||||
#else
|
||||
const ::boost::process::native_environment ne{};
|
||||
typedef typename ::boost::process::native_environment::const_entry_type value_type;
|
||||
const auto id = "PATH";
|
||||
static constexpr auto id = "PATH";
|
||||
#endif
|
||||
|
||||
auto itr = std::find_if(ne.cbegin(), ne.cend(),
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace detail {
|
||||
|
||||
struct throw_on_error_ : ::boost::process::detail::api::handler_base_ext
|
||||
{
|
||||
constexpr throw_on_error_() {};
|
||||
constexpr throw_on_error_() = default;
|
||||
|
||||
template <class Executor>
|
||||
void on_error(Executor&, const std::error_code & ec) const
|
||||
@@ -72,7 +72,7 @@ struct throw_on_error_ : ::boost::process::detail::api::handler_base_ext
|
||||
|
||||
struct ignore_error_ : ::boost::process::detail::api::handler_base_ext
|
||||
{
|
||||
constexpr ignore_error_() {};
|
||||
constexpr ignore_error_() = default;
|
||||
};
|
||||
|
||||
struct set_on_error : ::boost::process::detail::api::handler_base_ext
|
||||
@@ -81,7 +81,7 @@ struct set_on_error : ::boost::process::detail::api::handler_base_ext
|
||||
explicit set_on_error(std::error_code &ec) : ec_(ec) {}
|
||||
|
||||
template <class Executor>
|
||||
void on_error(Executor&, const std::error_code & ec) const
|
||||
void on_error(Executor&, const std::error_code & ec) const noexcept
|
||||
{
|
||||
ec_ = ec;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ private:
|
||||
|
||||
struct error_
|
||||
{
|
||||
constexpr error_() {}
|
||||
constexpr error_() = default;
|
||||
set_on_error operator()(std::error_code &ec) const {return set_on_error(ec);}
|
||||
set_on_error operator= (std::error_code &ec) const {return set_on_error(ec);}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define BOOST_PROCESS_EXTENSIONS_HPP_
|
||||
|
||||
#include <boost/process/detail/handler.hpp>
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
#include <boost/process/detail/windows/executor.hpp>
|
||||
@@ -62,6 +63,9 @@ using ::boost::process::detail::api::async_handler;
|
||||
using ::boost::process::detail::get_io_context;
|
||||
using ::boost::process::detail::get_last_error;
|
||||
using ::boost::process::detail::throw_last_error;
|
||||
using ::boost::process::detail::uses_handles;
|
||||
using ::boost::process::detail::foreach_used_handle;
|
||||
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;
|
||||
|
||||
107
include/boost/process/handles.hpp
Normal file
107
include/boost/process/handles.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2019 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_PROCESS_HANDLES_HPP_
|
||||
#define BOOST_PROCESS_HANDLES_HPP_
|
||||
|
||||
/**
|
||||
* \file boost/process/handles.hpp
|
||||
*
|
||||
* Defines functions to obtain handles of the current process and limit the amount for inherited ones.
|
||||
*/
|
||||
|
||||
#include <boost/process/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
#include <boost/process/detail/posix/handles.hpp>
|
||||
#elif defined(BOOST_WINDOWS_API)
|
||||
#include <boost/process/detail/windows/handles.hpp>
|
||||
#endif
|
||||
|
||||
#include <boost/process/detail/used_handles.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace this_process
|
||||
{
|
||||
|
||||
///The native type for handles
|
||||
using native_handle_type = ::boost::process::detail::api::native_handle_type;
|
||||
|
||||
/**
|
||||
* Get a snapshot of all handles of the process (i.e. file descriptors on posix and handles on windows) of the current process.
|
||||
*
|
||||
* \note This function might not work on certain posix systems.
|
||||
*
|
||||
* \note On Windows version older than windows 8 this function will iterate all the system handles, meaning it might be quite slow.
|
||||
*
|
||||
* \warning This functionality is utterly prone to race conditions, since other threads might open or close handles.
|
||||
*
|
||||
* \return The list of all open handles of the current process
|
||||
*/
|
||||
inline std::vector<native_handle_type> get_handles()
|
||||
{
|
||||
return ::boost::process::detail::api::get_handles();
|
||||
}
|
||||
|
||||
|
||||
/** \overload std::vector<native_handle_type> get_handles() */
|
||||
inline std::vector<native_handle_type> get_handles(std::error_code &ec)
|
||||
{
|
||||
return ::boost::process::detail::api::get_handles(ec);
|
||||
}
|
||||
|
||||
/** Determines if a given handle is a a stream-handle, i.e. any handle that can be used with read and write functions.
|
||||
* Stream handles include pipes, regular files and sockets.
|
||||
*
|
||||
* \return Indicates if it's a stream handle.
|
||||
*/
|
||||
inline bool is_stream_handle(native_handle_type handle)
|
||||
{
|
||||
return ::boost::process::detail::api::is_stream_handle(handle);
|
||||
}
|
||||
|
||||
|
||||
/** \overload bool is_stream_handle(native_handle_type handle) */
|
||||
inline bool is_stream_handle(native_handle_type handle, std::error_code &ec)
|
||||
{
|
||||
return ::boost::process::detail::api::is_stream_handle(handle, ec);
|
||||
}
|
||||
|
||||
}
|
||||
namespace process
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using limit_handles_ = ::boost::process::detail::api::limit_handles_;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* \code{.cpp}
|
||||
* system("gcc", limit_handles);
|
||||
* \endcode
|
||||
*
|
||||
* Since limit also closes the standard handles unless they are explicitly redirected they can be ignored by `limit_handles` in the following way.
|
||||
*
|
||||
* \code{.cpp}
|
||||
* system("gcc", limit_handles.allowStd())
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
const static ::boost::process::detail::api::limit_handles_ limit_handles;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BOOST_PROCESS_HANDLES_HPP_
|
||||
@@ -136,13 +136,13 @@ template<typename T> using is_mutable_buffer =
|
||||
>;
|
||||
|
||||
|
||||
struct null_t {constexpr null_t() {}};
|
||||
struct null_t {constexpr null_t() = default;};
|
||||
struct close_t;
|
||||
|
||||
template<class>
|
||||
struct std_in_
|
||||
{
|
||||
constexpr std_in_() {}
|
||||
constexpr std_in_() = default;
|
||||
|
||||
api::close_in close() const {return api::close_in(); }
|
||||
api::close_in operator=(const close_t &) const {return api::close_in();}
|
||||
@@ -199,7 +199,7 @@ struct std_in_
|
||||
template<int p1, int p2 = -1>
|
||||
struct std_out_
|
||||
{
|
||||
constexpr std_out_() {}
|
||||
constexpr std_out_() = default;
|
||||
|
||||
api::close_out<p1,p2> close() const {return api::close_out<p1,p2>(); }
|
||||
api::close_out<p1,p2> operator=(const close_t &) const {return api::close_out<p1,p2>();}
|
||||
@@ -260,7 +260,7 @@ struct std_out_
|
||||
|
||||
struct close_t
|
||||
{
|
||||
constexpr close_t() {}
|
||||
constexpr close_t() = default;
|
||||
template<int T, int U>
|
||||
api::close_out<T,U> operator()(std_out_<T,U>) {return api::close_out<T,U>();}
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace detail
|
||||
class codecvt_category_t : public std::error_category
|
||||
{
|
||||
public:
|
||||
codecvt_category_t(){}
|
||||
codecvt_category_t() = default;
|
||||
const char* name() const noexcept override {return "codecvt";}
|
||||
std::string message(int ev) const override
|
||||
{
|
||||
|
||||
@@ -120,6 +120,13 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
///Move Constructor
|
||||
basic_pipebuf(basic_pipebuf && ) = default;
|
||||
|
||||
///Destructor -> writes the frest of the data
|
||||
~basic_pipebuf()
|
||||
{
|
||||
if (basic_pipebuf::is_open())
|
||||
basic_pipebuf::overflow(Traits::eof());
|
||||
}
|
||||
|
||||
///Move construct from a pipe.
|
||||
basic_pipebuf(pipe_type && p) : _pipe(std::move(p)),
|
||||
_write(default_buffer_size),
|
||||
@@ -155,15 +162,17 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
///Writes characters to the associated output sequence from the put area
|
||||
int_type overflow(int_type ch = traits_type::eof()) override
|
||||
{
|
||||
if ((ch != traits_type::eof()) && _pipe.is_open())
|
||||
if (_pipe.is_open() && (ch != traits_type::eof()))
|
||||
{
|
||||
if (this->pptr() == this->epptr())
|
||||
{
|
||||
bool wr = this->_write_impl();
|
||||
*this->pptr() = ch;
|
||||
this->pbump(1);
|
||||
if (wr)
|
||||
{
|
||||
*this->pptr() = ch;
|
||||
this->pbump(1);
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -173,6 +182,9 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
else if (ch == traits_type::eof())
|
||||
this->sync();
|
||||
|
||||
return traits_type::eof();
|
||||
}
|
||||
///Synchronizes the buffers with the associated character sequence
|
||||
@@ -212,6 +224,36 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
|
||||
const pipe_type &pipe() const & {return _pipe;}
|
||||
///Get a rvalue reference to the pipe. Qualified as rvalue.
|
||||
pipe_type && pipe() && {return std::move(_pipe);}
|
||||
|
||||
///Check if the pipe is open
|
||||
bool is_open() const {return _pipe.is_open(); }
|
||||
|
||||
///Open a new pipe
|
||||
basic_pipebuf<CharT, Traits>* open()
|
||||
{
|
||||
if (is_open())
|
||||
return nullptr;
|
||||
_pipe = pipe();
|
||||
return this;
|
||||
}
|
||||
|
||||
///Open a new named pipe
|
||||
basic_pipebuf<CharT, Traits>* open(const std::string & name)
|
||||
{
|
||||
if (is_open())
|
||||
return nullptr;
|
||||
_pipe = pipe(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
///Flush the buffer & close the pipe
|
||||
basic_pipebuf<CharT, Traits>* close()
|
||||
{
|
||||
if (!is_open())
|
||||
return nullptr;
|
||||
overflow(Traits::eof());
|
||||
return this;
|
||||
}
|
||||
private:
|
||||
pipe_type _pipe;
|
||||
std::vector<char_type> _write;
|
||||
@@ -223,8 +265,13 @@ private:
|
||||
return false;
|
||||
|
||||
auto base = this->pbase();
|
||||
|
||||
if (base == this->pptr())
|
||||
return true;
|
||||
|
||||
std::ptrdiff_t wrt = _pipe.write(base,
|
||||
static_cast<typename pipe_type::int_type>(this->pptr() - base));
|
||||
|
||||
std::ptrdiff_t diff = this->pptr() - base;
|
||||
|
||||
if (wrt < diff)
|
||||
@@ -295,8 +342,9 @@ public:
|
||||
basic_ipstream& operator=(basic_ipstream && lhs)
|
||||
{
|
||||
std::basic_istream<CharT, Traits>::operator=(std::move(lhs));
|
||||
_buf = std::move(lhs);
|
||||
_buf = std::move(lhs._buf);
|
||||
std::basic_istream<CharT, Traits>::rdbuf(&_buf);
|
||||
return *this;
|
||||
};
|
||||
///Move assignment of a pipe.
|
||||
basic_ipstream& operator=(pipe_type && p)
|
||||
@@ -320,6 +368,33 @@ public:
|
||||
const pipe_type &pipe() const & {return _buf.pipe();}
|
||||
///Get a rvalue reference to the pipe. Qualified as rvalue.
|
||||
pipe_type && pipe() && {return std::move(_buf).pipe();}
|
||||
///Check if the pipe is open
|
||||
bool is_open() const {return _buf.is_open();}
|
||||
|
||||
///Open a new pipe
|
||||
void open()
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Open a new named pipe
|
||||
void open(const std::string & name)
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Flush the buffer & close the pipe
|
||||
void close()
|
||||
{
|
||||
if (_buf.close() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
}
|
||||
};
|
||||
|
||||
typedef basic_ipstream<char> ipstream;
|
||||
@@ -376,8 +451,9 @@ public:
|
||||
basic_opstream& operator=(basic_opstream && lhs)
|
||||
{
|
||||
std::basic_ostream<CharT, Traits>::operator=(std::move(lhs));
|
||||
_buf = std::move(lhs);
|
||||
_buf = std::move(lhs._buf);
|
||||
std::basic_ostream<CharT, Traits>::rdbuf(&_buf);
|
||||
return *this;
|
||||
};
|
||||
|
||||
///Move assignment of a pipe.
|
||||
@@ -402,6 +478,31 @@ public:
|
||||
const pipe_type &pipe() const & {return _buf.pipe();}
|
||||
///Get a rvalue reference to the pipe. Qualified as rvalue.
|
||||
pipe_type && pipe() && {return std::move(_buf).pipe();}
|
||||
|
||||
///Open a new pipe
|
||||
void open()
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Open a new named pipe
|
||||
void open(const std::string & name)
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Flush the buffer & close the pipe
|
||||
void close()
|
||||
{
|
||||
if (_buf.close() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
}
|
||||
};
|
||||
|
||||
typedef basic_opstream<char> opstream;
|
||||
@@ -459,8 +560,9 @@ public:
|
||||
basic_pstream& operator=(basic_pstream && lhs)
|
||||
{
|
||||
std::basic_istream<CharT, Traits>::operator=(std::move(lhs));
|
||||
_buf = std::move(lhs);
|
||||
_buf = std::move(lhs._buf);
|
||||
std::basic_iostream<CharT, Traits>::rdbuf(&_buf);
|
||||
return *this;
|
||||
};
|
||||
///Move assignment of a pipe.
|
||||
basic_pstream& operator=(pipe_type && p)
|
||||
@@ -484,6 +586,31 @@ public:
|
||||
const pipe_type &pipe() const & {return _buf.pipe();}
|
||||
///Get a rvalue reference to the pipe. Qualified as rvalue.
|
||||
pipe_type && pipe() && {return std::move(_buf).pipe();}
|
||||
|
||||
///Open a new pipe
|
||||
void open()
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Open a new named pipe
|
||||
void open(const std::string & name)
|
||||
{
|
||||
if (_buf.open() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
else
|
||||
this->clear();
|
||||
}
|
||||
|
||||
///Flush the buffer & close the pipe
|
||||
void close()
|
||||
{
|
||||
if (_buf.close() == nullptr)
|
||||
this->setstate(std::ios_base::failbit);
|
||||
}
|
||||
};
|
||||
|
||||
typedef basic_pstream<char> pstream;
|
||||
|
||||
@@ -16,6 +16,7 @@ if [ os.name ] = NT
|
||||
lib ws2_32 ;
|
||||
lib shell32 ;
|
||||
lib Advapi32 ;
|
||||
lib Ntdll ;
|
||||
}
|
||||
|
||||
project : requirements
|
||||
@@ -44,7 +45,7 @@ alias coroutine : /boost//coroutine : <link>static ;
|
||||
lib multi_ref : multi_ref1.cpp multi_ref2.cpp system : <target-os>windows:<source>shell32 ;
|
||||
|
||||
exe sparring_partner : sparring_partner.cpp program_options system filesystem iostreams :
|
||||
<warnings>off <target-os>windows:<source>shell32
|
||||
<warnings>off <target-os>windows:<source>shell32 <target-os>windows:<source>Ntdll
|
||||
;
|
||||
|
||||
exe exit_argc : exit_argc.cpp :
|
||||
@@ -55,7 +56,15 @@ exe sub_launch : sub_launcher.cpp program_options iostreams system filesystem :
|
||||
|
||||
rule test-options ( name )
|
||||
{
|
||||
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
|
||||
if "--boost-process-report-ci" in [ modules.peek : ARGV ]
|
||||
{
|
||||
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
|
||||
}
|
||||
else
|
||||
{
|
||||
return --log_level=error --report_level=detailed -- ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -66,10 +75,9 @@ test-suite bare :
|
||||
[ compile no_ansi_apps.cpp ]
|
||||
[ compile-fail spawn_fail.cpp ]
|
||||
[ compile-fail async_system_fail.cpp ]
|
||||
[ compile asio_no_deprecated.cpp ]
|
||||
;
|
||||
|
||||
echo [ test-options foo ] ;
|
||||
|
||||
test-suite with-valgrind :
|
||||
[ run async.cpp system thread filesystem : [ test-options async ] : sparring_partner ]
|
||||
[ run async_fut.cpp system thread filesystem : [ test-options async_fut ] : sparring_partner ]
|
||||
@@ -93,6 +101,7 @@ test-suite with-valgrind :
|
||||
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch ]
|
||||
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch : <build>no <target-os>windows:<build>yes <define>BOOST_USE_WINDOWS_H=1 : group-windows-h ]
|
||||
[ run group_wait.cpp system thread filesystem : [ test-options group_wait ] : sparring_partner ]
|
||||
[ run limit_fd.cpp program_options system filesystem : [ test-options limit_fd ] : sparring_partner ]
|
||||
[ run run_exe.cpp filesystem : : sparring_partner ]
|
||||
[ run run_exe_path.cpp filesystem : [ test-options run_exe_path ] : sparring_partner ]
|
||||
[ run search_path.cpp filesystem system : [ test-options search_path ] : : <target-os>windows:<source>shell32 ]
|
||||
|
||||
@@ -67,11 +67,11 @@ build_script:
|
||||
after_build:
|
||||
before_test:
|
||||
test_script:
|
||||
- ..\..\..\b2.exe address-model=64 architecture=x86 cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=.
|
||||
- ..\..\..\b2.exe address-model=64 architecture=x86 cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=. --boost-process-report-ci
|
||||
|
||||
after_test:
|
||||
on_success:
|
||||
on_failure:
|
||||
on_finish:
|
||||
- curl -s https://report.ci/upload.py | python - --name "windows test run" --root_dir=%BOOST%/libs/%PROJECT_TO_TEST%/test
|
||||
- curl -s https://report.ci/upload.py | python - --name "Windows test run" --root_dir=%BOOST%
|
||||
|
||||
|
||||
13
test/asio_no_deprecated.cpp
Normal file
13
test/asio_no_deprecated.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by kleme on 26.02.2018.
|
||||
//
|
||||
|
||||
#define BOOST_ASIO_NO_DEPRECATED 1
|
||||
#include <boost/process.hpp>
|
||||
int main() {}
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
#include <boost/process/posix.hpp>
|
||||
#else
|
||||
#include <boost/process/windows.hpp>
|
||||
#endif
|
||||
@@ -293,7 +293,7 @@ BOOST_AUTO_TEST_CASE(async_out_stream, *boost::unit_test::timeout(5))
|
||||
|
||||
std::string line;
|
||||
std::getline(istr, line);
|
||||
BOOST_REQUIRE_GE(line.size(), 3);
|
||||
BOOST_REQUIRE_GE(line.size(), 3u);
|
||||
BOOST_CHECK(boost::algorithm::starts_with(line, "abc"));
|
||||
c.wait();
|
||||
}
|
||||
@@ -376,4 +376,41 @@ BOOST_AUTO_TEST_CASE(async_error, *boost::unit_test::timeout(3))
|
||||
BOOST_CHECK(!exit_called);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
/*
|
||||
BOOST_AUTO_TEST_CASE(mixed_async, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
|
||||
boost::asio::deadline_timer timeout{io_context, boost::posix_time::seconds(2)};
|
||||
timeout.async_wait([&](boost::system::error_code ec){if (!ec) io_context.stop();});
|
||||
|
||||
bool exit_called = false;
|
||||
std::error_code ec;
|
||||
|
||||
bp::child c(master_test_suite().argv[1],
|
||||
"--wait", "1", "--exit-code", "42",
|
||||
ec,
|
||||
io_context,
|
||||
bp::on_exit([&](int exit, const std::error_code& ec_in)
|
||||
{
|
||||
timeout.cancel();
|
||||
exit_called=true;
|
||||
BOOST_CHECK_EQUAL(exit, 42);
|
||||
})
|
||||
);
|
||||
|
||||
BOOST_REQUIRE(!ec);
|
||||
std::thread thr([&]{c.wait();});
|
||||
io_context.run();
|
||||
|
||||
BOOST_CHECK(exit_called);
|
||||
BOOST_CHECK_EQUAL(c.exit_code(), 42);
|
||||
thr.join();
|
||||
|
||||
}*/
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -87,4 +87,61 @@ BOOST_AUTO_TEST_CASE(multithreaded_async_pipe)
|
||||
t.join();
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(move_pipe)
|
||||
{
|
||||
asio::io_context ios;
|
||||
|
||||
bp::async_pipe ap{ios};
|
||||
BOOST_TEST_CHECKPOINT("First move");
|
||||
bp::async_pipe ap2{std::move(ap)};
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
BOOST_CHECK_EQUAL(ap.native_source(), ::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
BOOST_CHECK_EQUAL(ap.native_sink (), ::boost::winapi::INVALID_HANDLE_VALUE_);
|
||||
#elif defined(BOOST_POSIX_API)
|
||||
BOOST_CHECK_EQUAL(ap.native_source(), -1);
|
||||
BOOST_CHECK_EQUAL(ap.native_sink (), -1);
|
||||
#endif
|
||||
|
||||
BOOST_TEST_CHECKPOINT("Second move");
|
||||
ap = std::move(ap2);
|
||||
|
||||
{
|
||||
BOOST_TEST_CHECKPOINT("Third move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
ap_inv.close();
|
||||
ap = std::move(ap_inv);
|
||||
}
|
||||
|
||||
{
|
||||
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
ap_inv.close();
|
||||
const auto ap3 = std::move(ap_inv);
|
||||
}
|
||||
|
||||
{
|
||||
//copy an a closed pipe
|
||||
BOOST_TEST_CHECKPOINT("Copy assign");
|
||||
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
ap_inv.close();
|
||||
ap = ap_inv; //copy an invalid pipe
|
||||
}
|
||||
|
||||
{
|
||||
//copy an a closed pipe
|
||||
BOOST_TEST_CHECKPOINT("Copy assign");
|
||||
BOOST_TEST_CHECKPOINT("Fourth move, from closed");
|
||||
bp::async_pipe ap_inv{ios};
|
||||
ap_inv.close();
|
||||
BOOST_TEST_CHECKPOINT("Copy construct");
|
||||
bp::async_pipe ap4{ap_inv};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -57,8 +57,7 @@ BOOST_AUTO_TEST_CASE(future_error, *boost::unit_test::timeout(15))
|
||||
|
||||
ios.run();
|
||||
|
||||
int exit_code = 0;
|
||||
BOOST_CHECK_THROW(exit_code = fut.get(), boost::system::system_error);
|
||||
BOOST_CHECK_THROW(fut.get(), boost::system::system_error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
#include <boost/asio/yield.hpp>
|
||||
|
||||
@@ -40,14 +41,15 @@ BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
|
||||
bp::async_system(
|
||||
ios, yield_,
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "123");
|
||||
"test", "--wait", "1");
|
||||
|
||||
BOOST_CHECK_EQUAL(ret, 123);
|
||||
BOOST_CHECK_EQUAL(ret, 0);
|
||||
BOOST_CHECK(did_something_else);
|
||||
};
|
||||
|
||||
boost::asio::spawn(ios, stackful);
|
||||
boost::asio::post(ios.get_executor(), [&]{did_something_else = true;});
|
||||
boost::asio::io_context::strand str{ios};
|
||||
boost::asio::post(str, [&]{boost::asio::spawn(ios, stackful);});
|
||||
boost::asio::post(str, [&]{did_something_else = true;});
|
||||
|
||||
ios.run();
|
||||
BOOST_CHECK(did_something_else);
|
||||
|
||||
@@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "42");
|
||||
|
||||
BOOST_CHECK_EQUAL(exit_code, 42);
|
||||
BOOST_CHECK_EQUAL(exit_code, 42u);
|
||||
BOOST_CHECK(did_something_else);
|
||||
}
|
||||
}
|
||||
@@ -68,4 +68,4 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -118,6 +118,29 @@ BOOST_AUTO_TEST_CASE(compare, *boost::unit_test::timeout(5))
|
||||
BOOST_TEST_PASSPOINT();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wcompare, *boost::unit_test::timeout(5))
|
||||
{
|
||||
auto nat = boost::this_process::wenvironment();
|
||||
bp::wenvironment env = nat;
|
||||
|
||||
{
|
||||
BOOST_CHECK_EQUAL(nat.size(), env.size());
|
||||
auto ni = nat.begin();
|
||||
auto ei = env.begin();
|
||||
|
||||
while ((ni != nat.end()) &&(ei != env.end()))
|
||||
{
|
||||
BOOST_CHECK_EQUAL(ni->get_name(), ei->get_name());
|
||||
BOOST_CHECK_EQUAL(ni->to_string(), ei->to_string());
|
||||
ni++; ei++;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_TEST_PASSPOINT();
|
||||
env.clear();
|
||||
BOOST_TEST_PASSPOINT();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(insert_remove, *boost::unit_test::timeout(5))
|
||||
{
|
||||
bp::environment env(boost::this_process::environment());
|
||||
|
||||
@@ -132,8 +132,28 @@ BOOST_AUTO_TEST_CASE(async_wait)
|
||||
windows::object_handle handle(io_context.get_executor(), c.native_handle());
|
||||
handle.async_wait(wait_handler(handle.native_handle(), wh_called));
|
||||
#endif
|
||||
std::cout << "async_wait 1" << std::endl;
|
||||
io_context.run();
|
||||
std::cout << "async_wait 2" << std::endl;
|
||||
BOOST_CHECK_MESSAGE(wh_called, "Wait handler not called");
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_nowait)
|
||||
{
|
||||
// No need to call wait when passing an io_context
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
using namespace boost::asio;
|
||||
|
||||
std::error_code ec;
|
||||
boost::asio::io_context io_context;
|
||||
bp::child c(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "221",
|
||||
ec,
|
||||
bp::on_exit=[](int exit_code, std::error_code) mutable {},
|
||||
io_context
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
io_context.run();
|
||||
BOOST_CHECK_EQUAL(221, c.exit_code());
|
||||
}
|
||||
@@ -29,7 +29,6 @@ struct run_exe
|
||||
{
|
||||
e.exe = exe.c_str();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct set_on_error
|
||||
@@ -74,13 +73,11 @@ struct overload_handler : ex::handler
|
||||
void on_setup(ex::windows_executor<Char, Sequence>& exec) const
|
||||
{
|
||||
st = "windows";
|
||||
const char* env = exec.env;
|
||||
}
|
||||
template <class Sequence>
|
||||
void on_setup(ex::posix_executor<Sequence>& exec) const
|
||||
{
|
||||
st = "posix";
|
||||
char** env = exec.env;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -58,10 +58,15 @@ BOOST_AUTO_TEST_CASE(group_test, *boost::unit_test::timeout(5))
|
||||
BOOST_CHECK(!c.running());
|
||||
if (c.running())
|
||||
c.terminate();
|
||||
|
||||
std::cout << "group_test out" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
|
||||
{
|
||||
std::cout << "attached" << std::endl;
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
bp::ipstream is;
|
||||
@@ -89,10 +94,11 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
|
||||
|
||||
|
||||
BOOST_REQUIRE(sub_c);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
|
||||
|
||||
|
||||
#if defined( BOOST_POSIX_API )
|
||||
::waitpid(sub_c.id(), nullptr, WNOHANG);
|
||||
BOOST_CHECK(kill(sub_c.id(), 0) == 0);
|
||||
#else
|
||||
BOOST_CHECK(sub_c.running());
|
||||
@@ -101,23 +107,27 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
|
||||
BOOST_REQUIRE_NO_THROW(g.terminate());
|
||||
|
||||
BOOST_CHECK(sub_c);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
|
||||
|
||||
BOOST_CHECK(!c.running());
|
||||
|
||||
#if defined( BOOST_POSIX_API )
|
||||
bool still_runs = kill(sub_c.id(), 0) == 0;
|
||||
#else
|
||||
errno = 0;
|
||||
::waitpid(sub_c.id(), nullptr, WNOHANG);
|
||||
bool still_runs = (kill(sub_c.id(), 0) == 0) && (errno != ECHILD) && (errno != ESRCH);
|
||||
#else
|
||||
bool still_runs = sub_c.running();
|
||||
#endif
|
||||
BOOST_CHECK_MESSAGE(!still_runs, boost::process::detail::get_last_error().message());
|
||||
|
||||
BOOST_CHECK(!still_runs);
|
||||
if (still_runs)
|
||||
sub_c.terminate();
|
||||
BOOST_CHECK(!c.running());
|
||||
if (c.running())
|
||||
c.terminate();
|
||||
|
||||
std::cout << "attached out" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -176,4 +186,6 @@ BOOST_AUTO_TEST_CASE(detached, *boost::unit_test::timeout(5))
|
||||
BOOST_CHECK(!c.running());
|
||||
if (c.running())
|
||||
c.terminate();
|
||||
|
||||
std::cerr << "detached out" << std::endl;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#define BOOST_TEST_MAIN
|
||||
#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
@@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15))
|
||||
|
||||
bp::child c2(
|
||||
master_test_suite().argv[1],
|
||||
"--wait", "3",
|
||||
"--wait", "4",
|
||||
g,
|
||||
ec
|
||||
);
|
||||
|
||||
180
test/limit_fd.cpp
Normal file
180
test/limit_fd.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) 2019 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_TEST_MAIN
|
||||
#define BOOST_TEST_IGNORE_SIGCHLD
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/process.hpp>
|
||||
#include <boost/process/handles.hpp>
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/io.hpp>
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#include <boost/process/extend.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <system_error>
|
||||
#include <string>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ip/udp.hpp>
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
#include <boost/winapi/get_current_thread.hpp>
|
||||
#include <boost/winapi/get_current_process.hpp>
|
||||
#endif
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace bp = boost::process;
|
||||
namespace bt = boost::this_process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
const auto get_handle = [](FILE * f) {return reinterpret_cast<bt::native_handle_type>(_get_osfhandle(_fileno(f)));};
|
||||
const auto socket_to_handle = [](::boost::winapi::UINT_PTR_ sock){return reinterpret_cast<::boost::winapi::HANDLE_>(sock);};
|
||||
#else
|
||||
const auto get_handle = [](FILE * f) {return fileno(f);};
|
||||
const auto socket_to_handle = [](int i){ return i;};
|
||||
#endif
|
||||
|
||||
std::error_code ec;
|
||||
auto fd_list = bt::get_handles(ec);
|
||||
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdin)), 1);
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stdout)), 1);
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), get_handle(stderr)), 1);
|
||||
|
||||
BOOST_CHECK(bt::is_stream_handle(get_handle(stdin), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(get_handle(stdout), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(get_handle(stderr), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
|
||||
BOOST_CHECK_GE(fd_list.size(), 3u);
|
||||
BOOST_CHECK_GE(bt::get_handles(ec).size(), fd_list.size());
|
||||
|
||||
bp::pipe p;
|
||||
|
||||
{
|
||||
|
||||
auto fd_list_new = bt::get_handles(ec);
|
||||
BOOST_CHECK_MESSAGE(!ec, ec);
|
||||
BOOST_CHECK_LE(fd_list.size() + 2u, fd_list_new.size());
|
||||
fd_list = std::move(fd_list_new);
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 1u);
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1u);
|
||||
|
||||
BOOST_CHECK(bt::is_stream_handle(p.native_source(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(p.native_sink(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
|
||||
p.close();
|
||||
fd_list = bt::get_handles(ec);
|
||||
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 0u);
|
||||
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0u);
|
||||
|
||||
#if defined( BOOST_WINDOWS_API )
|
||||
std::thread thr([]{});
|
||||
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
|
||||
int timer_fd = ::timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
|
||||
BOOST_CHECK(!bt::is_stream_handle(timer_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
#endif
|
||||
# if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
|
||||
int event_fd =::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
BOOST_CHECK(!bt::is_stream_handle(event_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
#endif
|
||||
int dir_fd = ::dirfd(::opendir("."));
|
||||
BOOST_CHECK(!bt::is_stream_handle(dir_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
#endif
|
||||
|
||||
|
||||
boost::asio::io_context ioc;
|
||||
boost::asio::ip::tcp::socket tcp_socket(ioc);
|
||||
boost::asio::ip::udp::socket udp_socket(ioc);
|
||||
bp::async_pipe ap(ioc);
|
||||
|
||||
tcp_socket.open(boost::asio::ip::tcp::v4());
|
||||
udp_socket.open(boost::asio::ip::udp::v4());
|
||||
|
||||
BOOST_CHECK(bt::is_stream_handle(socket_to_handle(tcp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(socket_to_handle(udp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(std::move(ap).sink(). native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
BOOST_CHECK(bt::is_stream_handle(std::move(ap).source().native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
}
|
||||
|
||||
struct on_setup_t
|
||||
{
|
||||
std::vector<bt::native_handle_type> &res;
|
||||
|
||||
on_setup_t(std::vector<bt::native_handle_type> & res) : res(res) {}
|
||||
template<typename Executor>
|
||||
void operator()(Executor & e)
|
||||
{
|
||||
bp::extend::foreach_used_handle(e, [this](bt::native_handle_type handle)
|
||||
{
|
||||
res.push_back(handle);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(iterate_handles, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::vector<bt::native_handle_type> res;
|
||||
|
||||
bp::pipe p_in;
|
||||
bp::pipe p_out;
|
||||
|
||||
auto source = p_in.native_source();
|
||||
auto sink = p_out.native_sink();
|
||||
std::error_code ec;
|
||||
|
||||
BOOST_WARN_NE(source, sink); //Sanity check
|
||||
|
||||
const auto ret = bp::system(master_test_suite().argv[1], "--exit-code" , "42",
|
||||
bp::std_in < p_out,
|
||||
bp::std_out > p_in,
|
||||
bp::extend::on_setup(on_setup_t(res)), ec);
|
||||
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
|
||||
BOOST_CHECK_EQUAL(ret, 42u);
|
||||
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0u);
|
||||
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0u);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5))
|
||||
{
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));};
|
||||
#else
|
||||
const auto get_handle = [](FILE * f){return std::to_string(fileno(f));};
|
||||
#endif
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS);
|
||||
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS);
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE);
|
||||
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS);
|
||||
|
||||
|
||||
}
|
||||
143
test/pipe.cpp
143
test/pipe.cpp
@@ -11,6 +11,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include <boost/process/pipe.hpp>
|
||||
#include <boost/process/environment.hpp>
|
||||
|
||||
using namespace std;
|
||||
namespace bp = boost::process;
|
||||
@@ -38,7 +39,8 @@ BOOST_AUTO_TEST_CASE(named, *boost::unit_test::timeout(2))
|
||||
#if defined( BOOST_WINDOWS_API )
|
||||
bp::pipe pipe("\\\\.\\pipe\\pipe_name");
|
||||
#elif defined( BOOST_POSIX_API )
|
||||
bp::pipe pipe("./test_pipe");
|
||||
const auto home_path = boost::this_process::environment()["HOME"].to_string();
|
||||
bp::pipe pipe(home_path + "/.boost_process_test_pipe");
|
||||
#endif
|
||||
|
||||
std::string in = "xyz";
|
||||
@@ -100,6 +102,110 @@ BOOST_AUTO_TEST_CASE(stream, *boost::unit_test::timeout(2))
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stream_move, *boost::unit_test::timeout(2))
|
||||
{
|
||||
|
||||
bp::pipe pipe;
|
||||
|
||||
bp::pstream os(pipe);
|
||||
bp::ipstream is(pipe);
|
||||
|
||||
int i = 42, j = 0, k = 0;
|
||||
|
||||
os << i << std::endl;
|
||||
os << std::endl;
|
||||
is >> j;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
|
||||
bp::pstream os2 = std::move(os);
|
||||
bp::ipstream is2 = std::move(is);
|
||||
os2 << i << std::endl;
|
||||
os2 << std::endl;
|
||||
is2 >> k;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, k);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ostream_move, *boost::unit_test::timeout(2))
|
||||
{
|
||||
|
||||
bp::pipe pipe;
|
||||
|
||||
bp::opstream os(pipe);
|
||||
bp::ipstream is(pipe);
|
||||
|
||||
int i = 42, j = 0, k = 0;
|
||||
|
||||
os << i << std::endl;
|
||||
os << std::endl;
|
||||
is >> j;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
|
||||
bp::opstream os2 = std::move(os);
|
||||
bp::ipstream is2 = std::move(is);
|
||||
os2 << i << std::endl;
|
||||
os2 << std::endl;
|
||||
is2 >> k;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, k);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stream_move_assignment, *boost::unit_test::timeout(2))
|
||||
{
|
||||
|
||||
bp::pipe pipe;
|
||||
|
||||
bp::pstream os(pipe);
|
||||
bp::ipstream is(pipe);
|
||||
|
||||
int i = 42, j = 0, k = 0;
|
||||
|
||||
os << i << std::endl;
|
||||
os << std::endl;
|
||||
is >> j;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
|
||||
bp::pstream os2;
|
||||
os2 = std::move(os);
|
||||
bp::ipstream is2;
|
||||
is2 = std::move(is);
|
||||
os2 << i << std::endl;
|
||||
os2 << std::endl;
|
||||
is2 >> k;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, k);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ostream_move_assignment, *boost::unit_test::timeout(2))
|
||||
{
|
||||
|
||||
bp::pipe pipe;
|
||||
|
||||
bp::opstream os(pipe);
|
||||
bp::ipstream is(pipe);
|
||||
|
||||
int i = 42, j = 0, k = 0;
|
||||
|
||||
os << i << std::endl;
|
||||
os << std::endl;
|
||||
is >> j;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
|
||||
bp::opstream os2;
|
||||
os2 = std::move(os);
|
||||
bp::ipstream is2;
|
||||
is2 = std::move(is);
|
||||
os2 << i << std::endl;
|
||||
os2 << std::endl;
|
||||
is2 >> k;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, k);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stream_line, *boost::unit_test::timeout(2))
|
||||
{
|
||||
|
||||
@@ -233,4 +339,37 @@ BOOST_AUTO_TEST_CASE(coverage, *boost::unit_test::timeout(5))
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stream_close, *boost::unit_test::timeout(5))
|
||||
{
|
||||
bp::pipe p;
|
||||
int i = 1234, j = 0;
|
||||
bp::opstream op{p};
|
||||
bp::ipstream ip{p};
|
||||
p.close();
|
||||
|
||||
op << i << " ";
|
||||
op.close();
|
||||
|
||||
ip >> j;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stream_close_scope, *boost::unit_test::timeout(5))
|
||||
{
|
||||
bp::pipe p;
|
||||
int i = 1234, j = 0;
|
||||
bp::ipstream ip;
|
||||
|
||||
{
|
||||
bp::opstream op{ip.pipe()};
|
||||
op << i << " ";
|
||||
}
|
||||
ip >> j;
|
||||
|
||||
BOOST_CHECK_EQUAL(i, j);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -51,6 +51,8 @@ BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(5))
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
BOOST_TEST_INFO("Launching child 2");
|
||||
|
||||
bp::child c2(
|
||||
master_test_suite().argv[1],
|
||||
bp::args={"test", "--prefix-once", "hello "},
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <boost/process.hpp>
|
||||
#include <boost/process/posix.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <system_error>
|
||||
|
||||
|
||||
@@ -21,6 +23,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace bp = boost::process;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bind_fd, *boost::unit_test::timeout(2))
|
||||
@@ -99,3 +102,67 @@ BOOST_AUTO_TEST_CASE(execve_throw_on_error, *boost::unit_test::timeout(2))
|
||||
BOOST_CHECK_EQUAL(e.code().value(), ENOENT);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
const auto pid = boost::this_process::get_id();
|
||||
const auto fd_path = fs::path("/proc") / std::to_string(pid) / "fd";
|
||||
|
||||
auto get_fds = [&]{
|
||||
std::vector<int> fds;
|
||||
for (auto && fd : fs::directory_iterator(fd_path))
|
||||
fds.push_back(std::stoi(fd.path().filename().string()));
|
||||
return fds;
|
||||
};
|
||||
|
||||
std::vector<int> fd_list = get_fds();
|
||||
if (fd_list.empty()) //then there's no /proc in the current linux distribution.
|
||||
return;
|
||||
|
||||
|
||||
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDOUT_FILENO) != fd_list.end());
|
||||
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDIN_FILENO) != fd_list.end());
|
||||
BOOST_CHECK(std::find(fd_list.begin(), fd_list.end(), STDERR_FILENO) != fd_list.end());
|
||||
|
||||
bp::pipe p; //should add two descriptors.
|
||||
|
||||
auto fd_list_new = get_fds();
|
||||
|
||||
BOOST_CHECK_EQUAL(fd_list_new.size(), fd_list.size() + 2);
|
||||
fd_list.push_back(p.native_source());
|
||||
fd_list.push_back(p.native_sink());
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL(
|
||||
bp::system(
|
||||
master_test_suite().argv[1],
|
||||
"test", "--exit-code", "123", ec), 123);
|
||||
|
||||
fd_list_new = get_fds();
|
||||
BOOST_CHECK_EQUAL(fd_list.size(), fd_list_new.size());
|
||||
|
||||
|
||||
const int native_source = p.native_source();
|
||||
BOOST_CHECK_EQUAL(
|
||||
bp::system(
|
||||
master_test_suite().argv[1],
|
||||
bp::std_in < p,
|
||||
"test", "--exit-code", "123", ec), 123);
|
||||
|
||||
BOOST_CHECK(!ec);
|
||||
|
||||
////now, p.source should be closed, so we remove it from fd_list
|
||||
|
||||
const auto itr = std::find(fd_list.begin(), fd_list.end(), native_source);
|
||||
if (itr != fd_list.end())
|
||||
fd_list.erase(itr);
|
||||
|
||||
fd_list_new = get_fds();
|
||||
|
||||
BOOST_CHECK_EQUAL(fd_list.size(), fd_list_new.size());
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
#include <boost/lambda/lambda.hpp>
|
||||
#include <boost/process/environment.hpp>
|
||||
#include <boost/process/handles.hpp>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
@@ -57,6 +58,7 @@ int main(int argc, char *argv[])
|
||||
("pwd", bool_switch())
|
||||
("query", value<std::string>())
|
||||
("stdin-to-stdout", bool_switch())
|
||||
("has-handle", value<std::uintptr_t>())
|
||||
#if defined(BOOST_POSIX_API)
|
||||
("posix-echo-one", value<std::vector<std::string> >()->multitoken())
|
||||
("posix-echo-two", value<std::vector<std::string> >()->multitoken());
|
||||
@@ -223,5 +225,15 @@ int main(int argc, char *argv[])
|
||||
std::cout << si.dwFlags << std::endl;
|
||||
}
|
||||
#endif
|
||||
else if (vm.count("has-handle"))
|
||||
{
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
const auto handle = reinterpret_cast<boost::this_process::native_handle_type>(vm["has-handle"].as<std::uintptr_t>());
|
||||
#else
|
||||
const auto handle = static_cast<boost::this_process::native_handle_type>(vm["has-handle"].as<std::uintptr_t>());
|
||||
#endif
|
||||
auto all_handles = boost::this_process::get_handles();
|
||||
return (std::find(all_handles.begin(), all_handles.end(), handle) != all_handles.end()) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -111,4 +111,26 @@ BOOST_AUTO_TEST_CASE(wait_until_ec)
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.message());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wait_for_exit_before_timeout)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
auto launch_time = std::chrono::system_clock::now();
|
||||
bp::child c(
|
||||
master_test_suite().argv[1],
|
||||
bp::args+={"test", "--wait", "1"},
|
||||
ec
|
||||
);
|
||||
BOOST_REQUIRE(!ec);
|
||||
|
||||
BOOST_CHECK(c.wait_for(std::chrono::seconds(20)));
|
||||
|
||||
auto timeout_t = std::chrono::system_clock::now();
|
||||
|
||||
// check that we didn't wait the entire timeout period
|
||||
BOOST_CHECK_LT(std::chrono::duration_cast<std::chrono::seconds>(timeout_t - launch_time).count(), 20);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(wargs, *boost::unit_test::timeout(2))
|
||||
std::error_code ec;
|
||||
bp::child c(
|
||||
master_test_suite().argv[1],
|
||||
L"test", "--echo-argv", L"hello thingy", "\"stuff\"", static_cast<const wchar_t*>(L" spa ce "),
|
||||
L"test", "--echo-argv", L"hello thingy", "\"stuff\"", static_cast<const wchar_t*>(L" spa\" ce "),
|
||||
bp::std_out>is,
|
||||
ec
|
||||
);
|
||||
@@ -62,9 +62,9 @@ BOOST_AUTO_TEST_CASE(wargs, *boost::unit_test::timeout(2))
|
||||
BOOST_CHECK_EQUAL(s, "\"stuff\"");
|
||||
|
||||
std::getline(is, s);
|
||||
s.resize(10);
|
||||
s.resize(11);
|
||||
|
||||
BOOST_CHECK_EQUAL(s, " spa ce ");
|
||||
BOOST_CHECK_EQUAL(s, " spa\" ce ");
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user