2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 16:52:14 +00:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Klemens David Morgenstern
83c2a986cb trait & fwd decl fixes 2019-04-10 13:41:08 +08:00
Klemens David Morgenstern
41fff78e00 Trying to add dynamic_buffer with sfinae as proposed in boostorg/process#56 2019-04-10 12:51:46 +08:00
79 changed files with 422 additions and 1795 deletions

View File

@@ -70,6 +70,7 @@ jobs:
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
python <(curl -s https://report.ci/upload.py) --name "Circle CI Gcc Tests" --framework boost
bash <(curl -s https://codecov.io/bash) -x gcov || true
echo "BUILD_RESULT: $FAILED"
exit $FAILED

View File

@@ -23,6 +23,8 @@ 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
@@ -71,7 +73,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; REPORT_CI=--boost-process-report-ci 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; USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
# Cloning Boost libraries (fast nondeep cloning)
- BOOST=$HOME/boost-local
- git init $BOOST
@@ -85,7 +87,7 @@ before_install:
- git checkout $BOOST_BRANCH
- git submodule update --init --merge
- git remote set-branches --add origin $BOOST_BRANCH
- git pull --recurse-submodules || true
- git pull --recurse-submodules
- git submodule update --init
- git checkout $BOOST_BRANCH
- git submodule foreach "git reset --quiet --hard; git clean -fxd"
@@ -101,8 +103,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=. $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
- ../../../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=.
after_success:
# Copying Coveralls data to a separate folder
- mkdir -p $TRAVIS_BUILD_DIR/coverals
@@ -131,6 +133,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"

View File

@@ -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 note, that if the same thread reads and writes to a pipe, it will only talk to itself.
Though please not, that if the the same thread reads and write to a pipe, it will only talk to itself.
[section:anonymous Anonymous Pipes]
The most common pipes are anonymous. Since they have no name,
The usual type of pipes, are the anonymous ones. Since the 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 no such encoding currently in the library.
there are more codes associated with that, but not so on windows. Therefore there is not 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 do 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 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]

View File

@@ -1,12 +1,12 @@
[section:design Design Rationale]
[section Scope]
This library is meant to give a wrapper around the different OS-specific methods
This library is meant to give an 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 available styles will be described. Note that the
In the following section the avaible 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 versions can be set explicitly:
several strings will yield a exe-args interpretation. Both version 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 is wanted with the cmd syntax, it ought to be escaped, i.e. '\\\"'. ]
If the same effect it 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]

View File

@@ -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 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
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
the executor gets passed.
[section:structure Structure]
The executor calls different handlers of the initializers during the process launch.
The basic structure consists of three functions, as given below:
The basic structure is 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 lambdas can also capture values, data can easily be shared between handlers.
Considering that lambda 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 defaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event.
Every handler not implemented dafaults 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__
{
template<typename Executor>
tempalte<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 handled directly. This is because
the behaviour can be changed through arguments passed to the launching function. Hence the executor
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
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 configuration of the executor, this may either throw, set an internal `error_code`, or do nothing.
Depending on the cofiguration 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 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.
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.
[note If you only write a partial implementation, e.g. only for __posix_executor__, the other variants will default to __handler__].

View File

@@ -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 automatic pipe-closing in this library, because
[note It is not possible to use automatically 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]

View File

@@ -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 is given (or the explicit form bp::cmd), it will be interpreted as a command line.
If a single string (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 until a time point with __wait_for__ and __wait_until__.]
[note You can also wait for a time span or a 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,7 +217,8 @@ 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.
[note You can do the same thing with [globalref boost::process::std_err std_err].]
[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]]
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
@@ -246,7 +247,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 output of nm is directly passed as input to c++filt
//we just use the same pipe, so the
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);
@@ -270,7 +271,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. For `nm` we might analyze the output line by line,
Now we get back to our compiling example. `nm` we might analyze it 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.
@@ -290,7 +291,7 @@ ios.run();
int result = c.exit_code();
```
To make it easier, boost.process provides a simpler interface for that, so that the buffer can be passed directly,
To make it easier, boost.process provides simpler interface for that, so that the buffer can be passed directly,
provided we also pass a reference to an io_service.
```
@@ -304,7 +305,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.
@@ -331,7 +332,7 @@ auto err = data.get();
[endsect]
[section:group Groups]
When launching several processes, they can be grouped together.
When launching several processes, processes 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,
@@ -342,7 +343,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 a program like `make`, which does launch its own child processes,
If we have 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:
@@ -412,14 +413,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 can be used in the example as following:
[globalref boost::process::env env] property, which the example as following:
```
bp::system("stuff", bp::env["VALUE_1"]="foo", bp::env["VALUE_2"]+={"bar1", "bar2"});
```
Please see the [headerref boost/process/environment.hpp reference] for more information.
Please see to the [headerref boost/process/environment.hpp reference] for more information.
[endsect]
[endsect]

View File

@@ -10,9 +10,6 @@
//[intro
#include <boost/process.hpp>
#include <string>
#include <iostream>
using namespace boost::process;
int main()

View File

@@ -30,7 +30,6 @@
#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>

View File

@@ -138,7 +138,7 @@ struct args_
arg_setter_<char, true> operator()(std::initializer_list<const char*> &&range) const
{
return arg_setter_<char, true>(range.begin(), range.end());
return arg_setter_<char>(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, false>(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
{
return arg_setter_<char, true>(range.begin(), range.end());
return arg_setter_<char>(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, false>(range.begin(), range.end());
return arg_setter_<char, true>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator()(std::initializer_list<const wchar_t*> &&range) const
{
return arg_setter_<wchar_t, true>(range.begin(), range.end());
return arg_setter_<wchar_t>(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, false>(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
{
return arg_setter_<wchar_t, true>(range.begin(), range.end());
return arg_setter_<wchar_t>(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, false>(range.begin(), range.end());
return arg_setter_<wchar_t, true>(range.begin(), range.end());
}
};

View File

@@ -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", ios, on_exit=[](int exit, const std::error_code& ec_in){});
child c("ls", on_exit=[](int exit, const std::error_code& ec_in){});
std::future<int> exit_code;
chlid c2("ls", ios, on_exit=exit_code);
chlid c2("ls", on_exit=exit_code);
\endcode

View File

@@ -47,8 +47,6 @@ 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.

View File

@@ -43,7 +43,7 @@ namespace boost { namespace process { namespace detail {
struct cmd_
{
constexpr cmd_() = default;
constexpr cmd_() {}
template<typename Char>
inline api::cmd_setter_<Char> operator()(const Char *s) const

View File

@@ -73,7 +73,7 @@ public:
template<typename ...Args>
explicit child(Args&&...args);
child() { } // Must be kept non defaulted for MSVC 14.1 & 14.2 #113
child() {}
child& operator=(const child&) = delete;
child& operator=(child && lhs)
{

View File

@@ -7,33 +7,54 @@
#define BOOST_PROCESS_DETAIL_POSIX_ASIO_FWD_HPP_
#include <memory>
#include <boost/asio/ts/netfwd.hpp>
namespace boost { namespace asio {
class mutable_buffer;
class mutable_buffers_1;
template<typename T>
struct is_mutable_buffer_sequence;
template<typename T>
struct is_const_buffer_sequence;
class const_buffer;
class const_buffers_1;
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<any_io_executor> signal_set;
typedef basic_signal_set<executor> signal_set;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
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<any_io_executor> stream_descriptor;
typedef basic_stream_descriptor<executor> stream_descriptor;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
} //posix
} //asio

View File

@@ -16,16 +16,13 @@
#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::uses_handles
::boost::process::detail::posix::require_io_context
{
Buffer & buf;
@@ -36,7 +33,6 @@ 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)
@@ -80,19 +76,9 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
template<typename Executor>
void on_setup(Executor & exec)
{
if (!pipe)
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
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)
{

View File

@@ -19,8 +19,6 @@
#include <memory>
#include <exception>
#include <future>
#include <array>
#include <boost/process/detail/used_handles.hpp>
namespace boost { namespace process { namespace detail { namespace posix {
@@ -47,24 +45,12 @@ 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::uses_handles
::boost::process::detail::posix::require_io_context
{
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)
{
@@ -145,11 +131,8 @@ struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
{
std::istream is (buffer_.get());
Type arg;
if (buffer_->size() > 0)
{
arg.resize(buffer_->size());
is.read(&*arg.begin(), buffer_->size());
}
arg.resize(buffer_->size());
is.read(&*arg.begin(), buffer_->size());
promise_->set_value(std::move(arg));
}
});

View File

@@ -23,7 +23,6 @@ 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) {}
@@ -71,8 +70,10 @@ public:
~async_pipe()
{
boost::system::error_code ec;
close(ec);
if (_sink .native_handle() != -1)
::close(_sink.native_handle());
if (_source.native_handle() != -1)
::close(_source.native_handle());
}
template<class CharT, class Traits = std::char_traits<CharT>>

View File

@@ -82,30 +82,22 @@ public:
int_type write(const char_type * data, int_type count)
{
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();
}
auto write_len = ::write(_sink, data, count * sizeof(char_type));
if (write_len == -1)
::boost::process::detail::throw_last_error();
return write_len;
}
int_type read(char_type * data, int_type count)
{
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();
}
auto read_len = ::read(_source, data, count * sizeof(char_type));
if (read_len == -1)
::boost::process::detail::throw_last_error();
return read_len;
}
bool is_open() const
bool is_open()
{
return (_source != -1) ||
(_sink != -1);

View File

@@ -12,11 +12,10 @@
#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, ::boost::process::detail::uses_handles
struct close_in : handler_base_ext
{
template <class Executor>
void on_exec_setup(Executor &e) const
@@ -24,9 +23,6 @@ struct close_in : handler_base_ext, ::boost::process::detail::uses_handles
if (::close(STDIN_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "close() failed");
}
int get_used_handles() {return STDIN_FILENO;}
};
}}}}

View File

@@ -10,9 +10,8 @@
#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 {
@@ -21,8 +20,6 @@ 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<>

View File

@@ -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 _env_impl;}
native_handle_type native_handle() const {return environ;}
};
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) -> bool
[&](const string_type & st)
{
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) -> bool
[&](const string_type & st)
{
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) -> bool
[&](const string_type & st)
{
if (st.size() <= id.size())
return false;
@@ -294,11 +294,7 @@ 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;

View File

@@ -274,10 +274,10 @@ class executor
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="))
while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
e++;
if ((e != nullptr) && (*e != nullptr))
if (e != nullptr)
{
std::vector<std::string> path;
boost::split(path, *e, boost::is_any_of(":"));
@@ -420,7 +420,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
_msg = "execve failed";
boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
_write_error(_pipe_sink);
_write_error(p.p[1]);
::close(p.p[1]);
_exit(EXIT_FAILURE);

View File

@@ -12,13 +12,11 @@
#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, ::boost::process::detail::uses_handles
struct close_fd_ : handler_base_ext
{
close_fd_(int fd) : fd_(fd) {}
@@ -29,15 +27,12 @@ struct close_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
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, ::boost::process::detail::uses_handles
struct close_fds_ : handler_base_ext
{
public:
close_fds_(const Range &fds) : fds_(fds) {}
@@ -53,8 +48,6 @@ public:
}
}
Range& get_used_handles() {return fds_;}
private:
Range fds_;
};
@@ -62,7 +55,7 @@ private:
template <class FileDescriptor>
struct bind_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
struct bind_fd_ : handler_base_ext
{
public:
bind_fd_(int id, const FileDescriptor &fd) : id_(id), fd_(fd) {}
@@ -74,9 +67,6 @@ 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_;

View File

@@ -13,22 +13,16 @@
#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, ::boost::process::detail::uses_handles
struct file_in : handler_base_ext
{
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)) {}

View File

@@ -13,13 +13,12 @@
#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 <unistd.h>
namespace boost { namespace process { namespace detail { namespace posix {
template<int p1, int p2>
struct file_out : handler_base_ext, ::boost::process::detail::uses_handles
struct file_out : handler_base_ext
{
file_descriptor file;
int handle = file.handle();
@@ -28,13 +27,6 @@ struct file_out : handler_base_ext, ::boost::process::detail::uses_handles
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;

View File

@@ -1,146 +0,0 @@
// 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

View File

@@ -14,21 +14,13 @@
#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, ::boost::process::detail::uses_handles
struct null_in : handler_base_ext
{
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

View File

@@ -13,27 +13,17 @@
#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>
#include <unistd.h>
namespace boost { namespace process { namespace detail { namespace posix {
template<int p1, int p2>
struct null_out : handler_base_ext, ::boost::process::detail::uses_handles
struct null_out : handler_base_ext
{
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<>

View File

@@ -13,23 +13,17 @@
#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, ::boost::process::detail::uses_handles
struct pipe_in : handler_base_ext
{
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())
@@ -54,9 +48,7 @@ struct pipe_in : handler_base_ext, ::boost::process::detail::uses_handles
{
if (::dup2(source, STDIN_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
if (source != STDIN_FILENO)
::close(source);
::close(source);
::close(sink);
}

View File

@@ -52,10 +52,8 @@ 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(), "dup2() failed");
if (sink != STDOUT_FILENO)
::close(sink);
e.set_error(::boost::process::detail::get_last_error(), "dup3() failed");
::close(sink);
::close(source);
}
@@ -65,9 +63,7 @@ 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");
if (sink != STDOUT_FILENO)
::close(sink);
::close(sink);
::close(source);
}
@@ -79,8 +75,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");
if ((sink != STDOUT_FILENO) && (sink != STDERR_FILENO))
::close(sink);
::close(sink);
::close(source);
}
class async_pipe;

View File

@@ -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::strand<boost::asio::io_context::executor_type> _strand{get_io_context().get_executor()};
boost::asio::io_context::strand _strand{get_io_context()};
boost::asio::signal_set _signal_set{get_io_context(), SIGCHLD};
std::vector<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;

View File

@@ -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(string_type s) : s_(std::move(s)) {}
start_dir_init(const string_type &s) : s_(s) {}
template <class PosixExecutor>
void on_exec_setup(PosixExecutor&) const

View File

@@ -27,7 +27,7 @@ inline void terminate(const child_handle &p, std::error_code &ec) noexcept
ec.clear();
int status;
::waitpid(p.pid, &status, WNOHANG); //just to clean it up
::waitpid(p.pid, &status, 0); //just to clean it up
}
inline void terminate(const child_handle &p)

View File

@@ -54,35 +54,11 @@ inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
::sigset_t sigset;
//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;
}
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
auto get_timespec =
[](const Duration & dur)
@@ -93,8 +69,8 @@ inline bool wait_until(
return ts;
};
int ret;
int status{0};
pid_t ret;
int status;
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
@@ -104,7 +80,6 @@ inline bool wait_until(
}
bool timed_out;
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
do
{
@@ -138,16 +113,9 @@ inline bool wait_until(
{
auto ts = get_timespec(time_out - Clock::now());
::timespec 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());
}
::nanosleep(&ts, &rem);
while (rem.tv_sec > 0 || rem.tv_nsec > 0)
::nanosleep(&rem, &rem);
::exit(0);
}
@@ -157,7 +125,7 @@ inline bool wait_until(
~child_cleaner_t()
{
int res;
::kill(pid, SIGKILL);
::kill(pid, -15);
::waitpid(pid, &res, WNOHANG);
}
};
@@ -165,21 +133,19 @@ inline bool wait_until(
do
{
int sig_{0};
int ret_sig = 0;
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
&& (WIFEXITED(status) || WIFSIGNALED(status)))
return false;
ret = ::sigwait(&sigset, &sig_);
&& (WIFEXITED(status) || WIFSIGNALED(status)))
ret_sig = ::sigwait(&sigset, nullptr);
errno = 0;
if ((sig_ == SIGCHLD) &&
ret = ::waitpid(p.pid, &status, WNOHANG);
if ((ret_sig == SIGCHLD) &&
(old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0) // == > is running
if (ret <= 0)
{
timed_out = Clock::now() >= time_out;
if (timed_out)

View File

@@ -59,33 +59,118 @@ 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;
::timespec sleep_interval;
sleep_interval.tv_sec = 0;
sleep_interval.tv_nsec = 100000000;
while (!(timed_out = (Clock::now() > time_out)))
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
{
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED | WNOHANG);
ec = get_last_error();
return false;
}
#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
if (ret == -1)
{
if ((errno == ECHILD) || (errno == ESRCH))
{
ec.clear();
return true;
}
ec = boost::process::detail::get_last_error();
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 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();
return false;
}
//we can wait, because unlike in the wait_for_exit, we have no race condition regarding eh exit code.
::nanosleep(&sleep_interval, nullptr);
//check if we're done
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
}
return !timed_out;
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
}
}
template< class Clock, class Duration >

View File

@@ -1,81 +0,0 @@
// 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_ */

View File

@@ -7,32 +7,57 @@
#define BOOST_PROCESS_DETAIL_WINDOWS_ASIO_FWD_HPP_
#include <memory>
#include <boost/asio/ts/netfwd.hpp>
namespace boost { namespace asio {
class mutable_buffer;
class mutable_buffers_1;
class const_buffer;
class const_buffers_1;
template<typename T>
struct is_mutable_buffer_sequence;
template<typename T>
struct is_const_buffer_sequence;
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<any_io_executor> stream_handle;
typedef basic_stream_handle<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<any_io_executor> object_handle;
typedef basic_object_handle<executor> object_handle;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
} //windows
} //asio

View File

@@ -17,20 +17,19 @@
#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::uses_handles
::boost::process::detail::windows::require_io_context
{
Buffer & buf;
@@ -43,11 +42,6 @@ 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)
{
}

View File

@@ -16,7 +16,6 @@
#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>
@@ -109,18 +108,12 @@ 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::uses_handles
::boost::process::detail::windows::require_io_context
{
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)
{

View File

@@ -40,27 +40,17 @@ class async_pipe
{
::boost::asio::windows::stream_handle _source;
::boost::asio::windows::stream_handle _sink ;
inline async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & ios_sink,
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(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());
inline async_pipe(const async_pipe& rhs);
async_pipe(async_pipe&& rhs) : _source(std::move(rhs._source)), _sink(std::move(rhs._sink))
@@ -284,7 +274,7 @@ async_pipe::async_pipe(const async_pipe& p) :
async_pipe::async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & ios_sink,
const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink)
const std::string & name) : _source(ios_source), _sink(ios_sink)
{
static constexpr int FILE_FLAG_OVERLAPPED_ = 0x40000000; //temporary
@@ -296,7 +286,7 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
#endif
::boost::winapi::PIPE_ACCESS_INBOUND_
| FILE_FLAG_OVERLAPPED_, //write flag
0, private_ ? 1 : ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
0, 1, 8192, 8192, 0, nullptr);
if (source == boost::winapi::INVALID_HANDLE_VALUE_)

View File

@@ -30,17 +30,14 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
{
std::string st = exe;
//put in quotes if it has spaces or double quotes
if(!exe.empty())
//put in quotes if it has spaces
{
auto it = st.find_first_of(" \"");
boost::replace_all(st, "\"", "\\\"");
if(it != st.npos)//contains spaces.
auto it = std::find(st.begin(), st.end(), ' ');
if (it != st.end())//contains spaces.
{
// double existing quotes
boost::replace_all(st, "\"", "\"\"");
// surround with quotes
st.insert(st.begin(), '"');
st += '"';
}
@@ -48,18 +45,15 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
for (auto & arg : data)
{
if(!arg.empty())
{
auto it = arg.find_first_of(" \"");//contains space or double quotes?
if(it != arg.npos)//yes
{
// double existing quotes
boost::replace_all(arg, "\"", "\"\"");
boost::replace_all(arg, "\"", "\\\"");
// surround with quotes
arg.insert(arg.begin(), '"');
arg += '"';
}
auto it = std::find(arg.begin(), arg.end(), ' ');//contains space?
if (it != arg.end())//ok, contains spaces.
{
//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.
}
if (!st.empty())//first one does not need a preceeding space
@@ -74,36 +68,30 @@ inline std::wstring build_args(const std::wstring & exe, std::vector<std::wstrin
{
std::wstring st = exe;
//put in quotes if it has spaces or double quotes
if(!exe.empty())
//put in quotes if it has spaces
{
auto it = st.find_first_of(L" \"");
boost::replace_all(st, L"\"", L"\\\"");
if(it != st.npos)//contains spaces or double quotes.
auto it = std::find(st.begin(), st.end(), L' ');
if (it != st.end())//contains spaces.
{
// 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)
{
if(!arg.empty())
{
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"\"\"");
boost::replace_all(arg, L"\"", L"\\\"");
// surround with quotes
arg.insert(arg.begin(), L'"');
arg += '"';
}
auto it = std::find(arg.begin(), arg.end(), L' ');//contains space?
if (it != arg.end())//ok, contains spaces.
{
//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.
}
if (!st.empty())//first one does not need a preceeding space

View File

@@ -98,7 +98,7 @@ public:
return static_cast<int_type>(read_len);
}
bool is_open() const
bool is_open()
{
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, ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
0, 1, 8192, 8192, 0, nullptr);
if (source == boost::winapi::INVALID_HANDLE_VALUE_)
::boost::process::detail::throw_last_error("create_named_pipe() failed");

View File

@@ -17,7 +17,7 @@
namespace boost { namespace process { namespace detail { namespace windows {
typedef ::boost::winapi::DWORD_ pid_t;
typedef int pid_t;
struct child_handle
{
@@ -66,7 +66,7 @@ struct child_handle
pid_t id() const
{
return static_cast<pid_t>(proc_info.dwProcessId);
return static_cast<int>(proc_info.dwProcessId);
}
typedef ::boost::winapi::HANDLE_ process_handle_t;

View File

@@ -11,7 +11,6 @@
#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 {
@@ -91,18 +90,10 @@ struct file_descriptor
}
file_descriptor(const file_descriptor & ) = delete;
file_descriptor(file_descriptor &&other)
: _handle( boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_) )
{
}
file_descriptor(file_descriptor && ) = default;
file_descriptor& operator=(const file_descriptor & ) = delete;
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& operator=(file_descriptor && ) = default;
~file_descriptor()
{

View File

@@ -13,20 +13,16 @@
#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,
::boost::process::detail::uses_handles
struct file_in : public ::boost::process::detail::handler_base
{
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)))) {}

View File

@@ -14,21 +14,16 @@
#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,
::boost::process::detail::uses_handles
struct file_out : public ::boost::process::detail::handler_base
{
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)))) {}

View File

@@ -6,10 +6,9 @@
#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/process/detail/used_handles.hpp>
#include <boost/winapi/process.hpp>
#include <boost/process/detail/windows/handler.hpp>
namespace boost { namespace process {
@@ -18,12 +17,10 @@ namespace detail { namespace windows {
struct group_ref : handler_base_ext, ::boost::process::detail::uses_handles
struct group_ref : handler_base_ext
{
::boost::winapi::HANDLE_ handle;
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
explicit group_ref(group_handle &g) :
handle(g.handle())
{}

View File

@@ -1,262 +0,0 @@
// 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_ */

View File

@@ -1,176 +0,0 @@
// 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

View File

@@ -14,18 +14,14 @@
#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, ::boost::process::detail::uses_handles
struct null_in : public ::boost::process::detail::handler_base
{
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

View File

@@ -14,18 +14,15 @@
#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, ::boost::process::detail::uses_handles
struct null_out : public ::boost::process::detail::handler_base
{
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;
};

View File

@@ -12,17 +12,14 @@
#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, ::boost::process::detail::uses_handles
struct pipe_in : public ::boost::process::detail::handler_base
{
::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

View File

@@ -13,7 +13,6 @@
#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 {
@@ -21,12 +20,10 @@ namespace boost { namespace process { namespace detail { namespace windows {
template<int p1, int p2>
struct pipe_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
struct pipe_out : public ::boost::process::detail::handler_base
{
::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())

View File

@@ -60,14 +60,14 @@ inline boost::filesystem::path search_path(
auto p = pp_ / filename;
for (boost::filesystem::path ext : extensions)
{
boost::filesystem::path pp_ext = p;
pp_ext += ext;
boost::filesystem::path pp = p;
pp += ext;
boost::system::error_code ec;
bool file = boost::filesystem::is_regular_file(pp_ext, ec);
bool file = boost::filesystem::is_regular_file(pp, ec);
if (!ec && file &&
::boost::winapi::sh_get_file_info(pp_ext.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))
::boost::winapi::sh_get_file_info(pp.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_))
{
return pp_ext;
return pp;
}
}
}

View File

@@ -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, static_cast<::boost::winapi::DWORD_>(wait_time)))
&completion_key, &overlapped, wait_time))
{
if (reinterpret_cast<::boost::winapi::HANDLE_>(completion_key) == p._job_object &&
completion_code == workaround::JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO_)

View File

@@ -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 data;
return std::move(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;
static constexpr auto id = L"PATH";
const auto id = L"PATH";
#else
const ::boost::process::native_environment ne{};
typedef typename ::boost::process::native_environment::const_entry_type value_type;
static constexpr auto id = "PATH";
const auto id = "PATH";
#endif
auto itr = std::find_if(ne.cbegin(), ne.cend(),

View File

@@ -59,7 +59,7 @@ namespace detail {
struct throw_on_error_ : ::boost::process::detail::api::handler_base_ext
{
constexpr throw_on_error_() = default;
constexpr throw_on_error_() {};
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_() = default;
constexpr ignore_error_() {};
};
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 noexcept
void on_error(Executor&, const std::error_code & ec) const
{
ec_ = ec;
}
@@ -92,7 +92,7 @@ private:
struct error_
{
constexpr error_() = default;
constexpr error_() {}
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);}

View File

@@ -7,7 +7,6 @@
#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>
@@ -63,9 +62,6 @@ 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;

View File

@@ -1,107 +0,0 @@
// 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_

View File

@@ -122,27 +122,13 @@ system("b2", std_out > null);
namespace boost { namespace process { namespace detail {
template<typename T> using is_streambuf = typename std::is_same<T, boost::asio::streambuf>::type;
template<typename T> using is_const_buffer =
std::integral_constant<bool,
std::is_same< boost::asio::const_buffer, T>::value |
std::is_base_of<boost::asio::const_buffer, T>::value
>;
template<typename T> using is_mutable_buffer =
std::integral_constant<bool,
std::is_same< boost::asio::mutable_buffer, T>::value |
std::is_base_of<boost::asio::mutable_buffer, T>::value
>;
struct null_t {constexpr null_t() = default;};
struct null_t {constexpr null_t() {}};
struct close_t;
template<class>
struct std_in_
{
constexpr std_in_() = default;
constexpr std_in_() {}
api::close_in close() const {return api::close_in(); }
api::close_in operator=(const close_t &) const {return api::close_in();}
@@ -177,19 +163,25 @@ struct std_in_
api::async_pipe_in operator=(async_pipe & p) const {return p;}
api::async_pipe_in operator<(async_pipe & p) const {return p;}
template<typename T, typename = typename std::enable_if<
is_const_buffer<T>::value || is_mutable_buffer<T>::value
>::type>
api::async_in_buffer<const T> operator=(const T & buf) const {return buf;}
template<typename T, typename = typename std::enable_if<is_streambuf<T>::value>::type >
api::async_in_buffer<T> operator=(T & buf) const {return buf;}
template<typename T>
auto operator=(const T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<const T>>::type {return buf;}
template<typename T>
auto operator<(const T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<const T>>::type {return buf;}
template<typename T, typename = typename std::enable_if<
is_const_buffer<T>::value || is_mutable_buffer<T>::value
>::type>
api::async_in_buffer<const T> operator<(const T & buf) const {return buf;}
template<typename T, typename = typename std::enable_if<is_streambuf<T>::value>::type >
api::async_in_buffer<T> operator<(T & buf) const {return buf;}
template<typename T>
auto operator=(T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<T>>::type {return buf;}
template<typename T>
auto operator<(T & buf) const -> typename std::enable_if<asio::is_const_buffer_sequence<T>::value, api::async_in_buffer<T>>::type {return buf;}
template<typename Allocator>
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator<(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename Allocator>
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator=(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename Allocator>
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator<(const boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename Allocator>
api::async_in_buffer<asio::basic_streambuf<Allocator>> operator=(const boost::asio::basic_streambuf<Allocator> & p) const {return p;}
};
@@ -199,7 +191,7 @@ struct std_in_
template<int p1, int p2 = -1>
struct std_out_
{
constexpr std_out_() = default;
constexpr std_out_() {}
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>();}
@@ -234,13 +226,24 @@ struct std_out_
api::async_pipe_out<p1, p2> operator=(async_pipe & p) const {return p;}
api::async_pipe_out<p1, p2> operator>(async_pipe & p) const {return p;}
api::async_out_buffer<p1, p2, const asio::mutable_buffer> operator=(const asio::mutable_buffer & buf) const {return buf;}
api::async_out_buffer<p1, p2, const asio::mutable_buffers_1> operator=(const asio::mutable_buffers_1 & buf) const {return buf;}
api::async_out_buffer<p1, p2, asio::streambuf> operator=(asio::streambuf & os) const {return os ;}
template<typename Allocator>
api::async_out_buffer<p1, p2, asio::basic_streambuf<Allocator>> operator=(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
template<typename Allocator>
api::async_out_buffer<p1, p2, asio::basic_streambuf<Allocator>> operator>(boost::asio::basic_streambuf<Allocator> & p) const {return p;}
api::async_out_buffer<p1, p2, const asio::mutable_buffer> operator>(const asio::mutable_buffer & buf) const {return buf;}
api::async_out_buffer<p1, p2, const asio::mutable_buffers_1> operator>(const asio::mutable_buffers_1 & buf) const {return buf;}
api::async_out_buffer<p1, p2, asio::streambuf> operator>(asio::streambuf & os) const {return os ;}
template<typename Buffer>
auto operator=(const Buffer & buf) const
-> typename std::enable_if<asio::is_mutable_buffer_sequence<Buffer>::value, api::async_out_buffer<p1, p2, Buffer>>::type
{
return buf;
}
template<typename Buffer>
auto operator>(const Buffer & buf) const
-> typename std::enable_if<asio::is_mutable_buffer_sequence<Buffer>::value, api::async_out_buffer<p1, p2, Buffer>>::type
{
return buf;
}
api::async_out_future<p1,p2, std::string> operator=(std::future<std::string> & fut) const { return fut;}
api::async_out_future<p1,p2, std::string> operator>(std::future<std::string> & fut) const { return fut;}
@@ -260,7 +263,7 @@ struct std_out_
struct close_t
{
constexpr close_t() = default;
constexpr close_t() {}
template<int T, int U>
api::close_out<T,U> operator()(std_out_<T,U>) {return api::close_out<T,U>();}
};

View File

@@ -28,7 +28,7 @@ namespace detail
class codecvt_category_t : public std::error_category
{
public:
codecvt_category_t() = default;
codecvt_category_t(){}
const char* name() const noexcept override {return "codecvt";}
std::string message(int ev) const override
{

View File

@@ -120,13 +120,6 @@ 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),
@@ -162,17 +155,15 @@ 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 (_pipe.is_open() && (ch != traits_type::eof()))
if ((ch != traits_type::eof()) && _pipe.is_open())
{
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
{
@@ -182,9 +173,6 @@ 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
@@ -224,36 +212,6 @@ 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;
@@ -265,13 +223,8 @@ 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)
@@ -342,9 +295,8 @@ public:
basic_ipstream& operator=(basic_ipstream && lhs)
{
std::basic_istream<CharT, Traits>::operator=(std::move(lhs));
_buf = std::move(lhs._buf);
_buf = std::move(lhs);
std::basic_istream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
basic_ipstream& operator=(pipe_type && p)
@@ -368,33 +320,6 @@ 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;
@@ -451,9 +376,8 @@ public:
basic_opstream& operator=(basic_opstream && lhs)
{
std::basic_ostream<CharT, Traits>::operator=(std::move(lhs));
_buf = std::move(lhs._buf);
_buf = std::move(lhs);
std::basic_ostream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
@@ -478,31 +402,6 @@ 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;
@@ -560,9 +459,8 @@ public:
basic_pstream& operator=(basic_pstream && lhs)
{
std::basic_istream<CharT, Traits>::operator=(std::move(lhs));
_buf = std::move(lhs._buf);
_buf = std::move(lhs);
std::basic_iostream<CharT, Traits>::rdbuf(&_buf);
return *this;
};
///Move assignment of a pipe.
basic_pstream& operator=(pipe_type && p)
@@ -586,31 +484,6 @@ 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;

View File

@@ -16,7 +16,6 @@ if [ os.name ] = NT
lib ws2_32 ;
lib shell32 ;
lib Advapi32 ;
lib Ntdll ;
}
project : requirements
@@ -45,7 +44,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 <target-os>windows:<source>Ntdll
<warnings>off <target-os>windows:<source>shell32
;
exe exit_argc : exit_argc.cpp :
@@ -56,15 +55,8 @@ exe sub_launch : sub_launcher.cpp program_options iostreams system filesystem :
rule test-options ( name )
{
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 -- ;
}
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
#return --log_level=error --report_level=detailed -- ;
}
@@ -101,7 +93,6 @@ 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 ]

View File

@@ -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=. --boost-process-report-ci
- ..\..\..\b2.exe address-model=64 architecture=x86 cxxflags="-DBOOST_TRAVISCI_BUILD" -sBOOST_BUILD_PATH=.
after_test:
on_success:
on_failure:
on_finish:
- curl -s https://report.ci/upload.py | python - --name "Windows test run" --root_dir=%BOOST%
- curl -s https://report.ci/upload.py | python - --name "windows test run" --root_dir=%BOOST%/libs/%PROJECT_TO_TEST%/test

View File

@@ -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(), 3u);
BOOST_REQUIRE_GE(line.size(), 3);
BOOST_CHECK(boost::algorithm::starts_with(line, "abc"));
c.wait();
}
@@ -413,4 +413,4 @@ BOOST_AUTO_TEST_CASE(mixed_async, *boost::unit_test::timeout(5))
}*/
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -96,13 +96,8 @@ BOOST_AUTO_TEST_CASE(move_pipe)
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);

View File

@@ -57,7 +57,8 @@ BOOST_AUTO_TEST_CASE(future_error, *boost::unit_test::timeout(15))
ios.run();
BOOST_CHECK_THROW(fut.get(), boost::system::system_error);
int exit_code = 0;
BOOST_CHECK_THROW(exit_code = fut.get(), boost::system::system_error);
}
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -41,9 +41,9 @@ BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
bp::async_system(
ios, yield_,
master_test_suite().argv[1],
"test", "--wait", "1");
"test", "--exit-code", "123");
BOOST_CHECK_EQUAL(ret, 0);
BOOST_CHECK_EQUAL(ret, 123);
BOOST_CHECK(did_something_else);
};

View File

@@ -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, 42u);
BOOST_CHECK_EQUAL(exit_code, 42);
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();

View File

@@ -118,29 +118,6 @@ 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());

View File

@@ -29,6 +29,7 @@ struct run_exe
{
e.exe = exe.c_str();
}
};
struct set_on_error
@@ -73,11 +74,13 @@ 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;
}
};

View File

@@ -58,15 +58,10 @@ 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;
@@ -94,11 +89,10 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
BOOST_REQUIRE(sub_c);
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //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());
@@ -107,27 +101,23 @@ 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(100)); //just to be sure.
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
BOOST_CHECK(!c.running());
#if defined( BOOST_POSIX_API )
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 = kill(sub_c.id(), 0) == 0;
#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;
}
@@ -186,6 +176,4 @@ 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;
}

View File

@@ -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", "4",
"--wait", "3",
g,
ec
);

View File

@@ -1,180 +0,0 @@
// 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);
}

View File

@@ -102,110 +102,6 @@ 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))
{
@@ -339,37 +235,4 @@ BOOST_AUTO_TEST_CASE(coverage, *boost::unit_test::timeout(5))
}
}
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();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -17,7 +17,6 @@
#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>
@@ -58,7 +57,6 @@ 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());
@@ -225,15 +223,5 @@ 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;
}

View File

@@ -111,26 +111,4 @@ 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();

View File

@@ -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(11);
s.resize(10);
BOOST_CHECK_EQUAL(s, " spa\" ce ");
BOOST_CHECK_EQUAL(s, " spa ce ");
}