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

Compare commits

...

62 Commits

Author SHA1 Message Date
Klemens David Morgenstern
0a554c92b5 Merge branch 'develop' of github.com:boostorg/process into develop 2019-05-18 11:11:20 +07:00
Klemens Morgenstern
fa81cecffc Merge pull request #196 from klemens-morgenstern/limit_fd
Limit fd
2019-05-18 11:07:55 +07:00
Klemens David Morgenstern
977b76f6aa Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2019-05-15 12:31:16 +07:00
Klemens Morgenstern
fc1acb82d9 Merge pull request #199 from klemens-morgenstern/stream_close
added opstream::close, closes klemens-morgenstern/boost-process#198
2019-05-14 22:04:21 +07:00
Klemens David Morgenstern
db7af9f87d doc addition 2019-05-14 13:22:05 +07:00
Klemens Morgenstern
a350cc346b Merge pull request #203 from klemens-morgenstern/win_pipe_unlimited_instances
Closes boostorg/process#84 and boostorg/process#83
2019-05-14 12:41:29 +07:00
Klemens David Morgenstern
9fa86d3d65 removed call to 'close_sink' 2019-05-14 12:15:57 +07:00
Klemens David Morgenstern
e426f2bfac remove ill-formed test & minor fixes 2019-05-14 11:57:20 +07:00
Klemens David Morgenstern
6b173117aa Merge branch 'develop' into stream_close 2019-05-14 10:45:53 +07:00
Klemens David Morgenstern
ecbc93408f Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-14 10:45:16 +07:00
Klemens David Morgenstern
6ba9a48d15 Merge branch 'develop' into new_wait_for_test 2019-05-14 10:44:40 +07:00
Klemens David Morgenstern
519c0a636a Merge branch 'develop' into limit_fd 2019-05-14 10:44:08 +07:00
Klemens David Morgenstern
82195c61af Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop
# Conflicts:
#	include/boost/process/detail/posix/wait_group.hpp
2019-05-14 00:41:41 +07:00
Klemens David Morgenstern
c604e3a20e switched to a wait/sleep version for OSX - should work fine for groups 2019-05-14 00:36:24 +07:00
Klemens Morgenstern
b27d0170ba Typo fix 2019-05-13 23:50:31 +07:00
Klemens David Morgenstern
98fd4eecf0 small reordering of code 2019-05-13 20:43:03 +07:00
Klemens David Morgenstern
3799315ce7 reverted to pipes for OSX 2019-05-13 20:11:17 +07:00
Klemens David Morgenstern
b9431ba492 fixing signal workaround 2019-05-13 10:30:28 +07:00
Klemens David Morgenstern
c0dca35615 added wrongfully removed build script options 2019-05-13 00:28:10 +07:00
Klemens David Morgenstern
790d79db9c Merge branch 'develop' into limit_fd 2019-05-13 00:23:52 +07:00
Klemens David Morgenstern
5de0a795d1 fixed wait_for_exit 2019-05-13 00:22:46 +07:00
Klemens David Morgenstern
cbaa913e3d Merge branch 'develop' into limit_fd
# Conflicts:
#	include/boost/process/detail/posix/pipe_out.hpp
#	test/Jamfile.jam
2019-05-12 17:43:40 +07:00
Klemens David Morgenstern
5786162fb5 Merge branch 'develop' into new_wait_for_test 2019-05-12 17:42:08 +07:00
Klemens David Morgenstern
78c44dd560 Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-12 17:40:54 +07:00
Klemens David Morgenstern
23ff67d83d added space for pipe close test 2019-05-12 17:37:35 +07:00
Klemens David Morgenstern
476c6ccd95 Merge branch 'develop' into stream_close 2019-05-12 17:13:36 +07:00
Klemens David Morgenstern
28126b3432 osx fix 2019-05-12 17:02:25 +07:00
Klemens David Morgenstern
ed8388d091 actual fix 2019-05-12 16:55:45 +07:00
Klemens David Morgenstern
43c402a5da test logic fix 2019-05-12 16:53:54 +07:00
Klemens David Morgenstern
9ff2f6f3ef still trying for circleci 2019-05-10 16:56:11 +07:00
Klemens David Morgenstern
b2a0fadaca Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	.travis.yml
2019-05-10 16:38:23 +07:00
Klemens David Morgenstern
3c4057204e hopefully fixed wait_for for osx 2019-05-10 16:34:51 +07:00
Klemens David Morgenstern
717ac47510 further investigation of circlci error 2019-05-10 12:21:37 +07:00
Klemens David Morgenstern
4f6f4eb391 updated for circle 2019-05-10 12:00:43 +07:00
Klemens David Morgenstern
99e04036c7 removed report-ci from circle-ci 2019-05-10 00:37:00 +07:00
Klemens David Morgenstern
519e339365 added more checks 2019-05-09 23:05:09 +07:00
Klemens David Morgenstern
d2c930470f removed && false 2019-05-09 17:08:58 +07:00
Klemens David Morgenstern
d5709ae747 trying to fix wait_for 2019-05-09 16:35:58 +07:00
Klemens David Morgenstern
d2ab81b1b9 Merge branch 'develop' into new_wait_for_test
# Conflicts:
#	include/boost/process/detail/posix/wait_for_exit.hpp
#	include/boost/process/detail/posix/wait_group.hpp
2019-05-09 12:20:20 +07:00
Klemens David Morgenstern
885557fe01 still trying to make wait_for work 2019-05-09 12:19:26 +07:00
Klemens David Morgenstern
b5b758f89a Merge branch 'develop' into stream_close 2019-05-09 11:29:31 +07:00
Klemens David Morgenstern
2a954eb809 Merge branch 'develop' into win_pipe_unlimited_instances 2019-05-09 11:24:14 +07:00
Klemens David Morgenstern
9e3fdc9669 SIGSEV fix 2019-05-08 16:44:38 +07:00
Klemens David Morgenstern
6263e74bcd Merge branch 'develop' into limit_fd 2019-05-07 12:16:53 +07:00
Klemens David Morgenstern
faae08ee64 replaced 1 with ::boost::winapi::HANDLE_FLAG_INHERIT_ 2019-05-07 12:16:32 +07:00
Klemens David Morgenstern
cfd0fc055c fixed overflow func 2019-05-07 11:48:16 +07:00
Klemens David Morgenstern
849b5d0f30 Closes boostorg/process#84 and boostorg/process#83 2019-05-07 10:59:50 +07:00
Klemens Morgenstern
d13df2a194 Merge pull request #82 from Parean/develop
should close klemens-morgenstern/boost-process#201
2019-05-03 21:07:53 +07:00
Denis Smirnov
86fc3b0b4d should close klemens-morgenstern/boost-process#201 2019-05-03 19:07:17 +07:00
Klemens David Morgenstern
4733ca719f typo fix 2019-04-25 22:40:13 +07:00
Klemens David Morgenstern
296f12eb64 RAII issues & resetting flags on windwos handles 2019-04-25 22:36:30 +07:00
Klemens David Morgenstern
96d3470e37 added this-> before clear() 2019-04-25 22:06:23 +07:00
Klemens David Morgenstern
2265c98d81 added missing ; 2019-04-25 20:24:42 +07:00
Klemens David Morgenstern
060e5c2526 fixed name lookup 2019-04-25 15:15:29 +07:00
Klemens David Morgenstern
b4894807f1 added opstream::close, closes klemens-morgenstern/boost-process#198 2019-04-24 18:24:44 +07:00
Klemens David Morgenstern
5e90c8de9b fixed up tests 2019-04-10 10:09:32 +08:00
Klemens David Morgenstern
a486a25a07 appveyor minor fix, pipe_out naming fix & limit_fd small sanity check because of weird osx bug 2019-04-10 01:33:21 +08:00
Klemens David Morgenstern
f8c0dd4da5 prototype for limit_fd 2019-04-09 23:39:43 +08:00
Klemens David Morgenstern
ee6870bfbc added used_handles & limit_fd for windows 2019-04-09 19:16:55 +08:00
Klemens David Morgenstern
0422b6bfb8 added posix get_handle function 2019-04-09 15:40:26 +08:00
Klemens David Morgenstern
7fc41b2815 added windows handles functionality 2019-04-09 14:31:50 +08:00
Klemens David Morgenstern
a49f1f6e2d Started on limit-fd functionality 2019-04-08 11:11:55 +08:00
45 changed files with 1440 additions and 158 deletions

View File

@@ -70,8 +70,6 @@ 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
cd ../../..
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
bash <(curl -s https://codecov.io/bash) -x gcov > /dev/null || true
echo "BUILD_RESULT: $FAILED"
exit $FAILED

View File

@@ -23,8 +23,6 @@ os:
- linux
- osx
secure: "vs7qgXb0lQg8CTyDPSi3RQtOIOtssaCkBIx86UoEvTXwJCTOLPe7ZufQ0lobn0OVWo261AMx9GbumBBzqfsvJc1G6ixGBVwymiGli/R8DZDvvg9UdljsEk65s/XbujE/9qh97zKGGioFyCn1Bmf5+SdDAxsuXTZm/cBny5VxYaaCR7s2cFUmp4up/djqg1GI7uwBh3ceodT3OL1X3dlMV59gOJWWNsB+RO9b9DPhTW7nOlMNRiEFik4rweecQB0JS8LaHDjYwzIRrGYHX+lR9cE/O8GCCHcUOmq9jCozDdxx+HZRu4rb1ST1RiDbvYaoeTif0Df1fVXHWOoO2D4NlXB6tJPXw2mkop00j6zkcydUJYid6T1lwfEpXAhd5A9FvOIXO5hoju1wlqfkU2eFQ9Na8z8bCIX2niZmveZWp4Ag52gEPzJMFx9hHGT8J4FWMvkqTWezux1sPZrjZjc0kXdJrIp84D9MsBc1sKrxOAOb5ekSfIK5n4JDkgUtuwMSTvEdWqNJXFPZq1rEu4GTwX99z3/XF+pM5XaCDQtZ/zUA5SPHhy0dKLH/BvceUqLJt53+lMcpsltJDB+XxQ/CFL7IdgR91OKGus/z4dbVWiSdkoNvcuZqjQLFLOMVNxoqC6PRvDAEhpy21j/5GUPvM5baQS7IEin0NF7bOTtXJdY="
env:
matrix:
- BADGE=linux
@@ -73,7 +71,7 @@ before_install:
# Set this to the name of the library
- PROJECT_TO_TEST=`basename $TRAVIS_BUILD_DIR`
- echo "Testing $PROJECT_TO_TEST"
- if [ $TRAVIS_OS_NAME = "osx" ]; then brew install gcc5; brew install valgrind; brew install llvm; TOOLSET=clang; BOOST_TEST_CATCH_SYSTEM_ERRORS=no; MULTITHREAD=-j8; else TOOLSET=gcc-5; USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
- if [ $TRAVIS_OS_NAME = "osx" ]; then brew install gcc5; brew install valgrind; brew install llvm; TOOLSET=clang; BOOST_TEST_CATCH_SYSTEM_ERRORS=no; MULTITHREAD=-j8; else TOOLSET=gcc-5; REPORT_CI=--boost-process-report-ci USE_VALGRIND="testing.launcher=valgrind valgrind=on"; fi
# Cloning Boost libraries (fast nondeep cloning)
- BOOST=$HOME/boost-local
- git init $BOOST
@@ -103,10 +101,8 @@ before_install:
- echo BOOST_TEST_CATCH_SYSTEM_ERRORS $BOOST_TEST_CATCH_SYSTEM_ERRORS
script:
# `--coverage` flags required to generate coverage info for Coveralls
- ../../../b2 $MULTITHREAD with-valgrind address-model=64 architecture=x86 $USE_VALGRIND toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
- ../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=.
- cat /home/travis/boost-local/libs/boost-process/test/report_wait_for.xml
- cat /home/travis/boost-local/libs/boost-process/test/log_wait_for.xml
- ../../../b2 $MULTITHREAD with-valgrind address-model=64 architecture=x86 $USE_VALGRIND toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI
- ../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI
after_success:
# Copying Coveralls data to a separate folder
- mkdir -p $TRAVIS_BUILD_DIR/coverals

View File

@@ -30,7 +30,7 @@
[def asio_async_read [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/async_read.html boost::asio::async_read]]
[def bp::environment [classref boost::process::basic_environment bp::environment]]
[def bp::native_environment [classref boost::process::basic_native_environment bp::native_environment]]
[def boost::this_process::environment [funcref boost::this_process::environment boost::this_process::environment]]
[def boost::this_process::environment [funcref boost::this_process::environment boost::this_process:deadlock :environment]]
[def std::chrono::seconds [@http://en.cppreference.com/w/cpp/chrono/duration std::chrono::seconds]]
[def std::vector [@http://en.cppreference.com/w/cpp/container/vector std::vector]]
@@ -217,8 +217,7 @@ std::vector<std::string> read_outline(std::string & file)
What this does is redirect the `stdout` of the process into a pipe and we read this
synchronously.
[warning The pipe will cause a deadlock if you try to read after nm exited]
[note You can do the same thing with [globalref boost::process::std_err std_err]]
[note You can do the same thing with [globalref boost::process::std_err std_err]]
Now we get the name from `nm` and we might want to demangle it, so we use input and output.
`nm` has a demangle option, but for the sake of the example, we'll use

View File

@@ -30,6 +30,7 @@
#include <boost/process/error.hpp>
#include <boost/process/exe.hpp>
#include <boost/process/group.hpp>
#include <boost/process/handles.hpp>
#include <boost/process/io.hpp>
#include <boost/process/pipe.hpp>
#include <boost/process/shell.hpp>

View File

@@ -16,13 +16,16 @@
#include <boost/process/async_pipe.hpp>
#include <memory>
#include <future>
#include <boost/process/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
template<typename Buffer>
struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
::boost::process::detail::posix::require_io_context
::boost::process::detail::posix::require_io_context,
::boost::process::detail::uses_handles
{
Buffer & buf;
@@ -33,6 +36,7 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
fut = promise->get_future(); return std::move(*this);
}
std::shared_ptr<boost::process::async_pipe> pipe;
async_in_buffer(Buffer & buf) : buf(buf)
@@ -76,9 +80,19 @@ struct async_in_buffer : ::boost::process::detail::posix::handler_base_ext,
template<typename Executor>
void on_setup(Executor & exec)
{
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
if (!pipe)
pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
}
std::array<int, 3> get_used_handles()
{
if (pipe)
return {STDIN_FILENO, pipe->native_source(), pipe->native_sink()};
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
return {STDIN_FILENO, STDIN_FILENO, STDIN_FILENO};
}
template <typename Executor>
void on_exec_setup(Executor &exec)
{

View File

@@ -19,6 +19,8 @@
#include <memory>
#include <exception>
#include <future>
#include <array>
#include <boost/process/detail/used_handles.hpp>
namespace boost { namespace process { namespace detail { namespace posix {
@@ -45,12 +47,24 @@ inline int apply_out_handles(int handle, std::integral_constant<int, 1>, std::in
template<int p1, int p2, typename Buffer>
struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
::boost::process::detail::posix::require_io_context
::boost::process::detail::posix::require_io_context,
::boost::process::detail::uses_handles
{
Buffer & buf;
std::shared_ptr<boost::process::async_pipe> pipe;
std::array<int, 4> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
if (pipe)
return {pipe->native_source(), pipe->native_sink(), pp1, pp2};
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
return {pp1, pp2, pp1, pp2};
}
async_out_buffer(Buffer & buf) : buf(buf)
{

View File

@@ -97,7 +97,7 @@ public:
return read_len;
}
bool is_open()
bool is_open() const
{
return (_source != -1) ||
(_sink != -1);

View File

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

View File

@@ -10,8 +10,9 @@
#ifndef BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
#define BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/posix/handler.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
@@ -20,6 +21,8 @@ struct close_out : handler_base_ext
{
template <class Executor>
inline void on_exec_setup(Executor &e) const;
std::array<int, 2> get_used_handles() {return {p1 != -1 ? p1 : p2, p2 != -1 ? p2 : p1};}
};
template<>

View File

@@ -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(p.p[1]);
_write_error(_pipe_sink);
::close(p.p[1]);
_exit(EXIT_FAILURE);

View File

@@ -12,11 +12,13 @@
#include <boost/process/detail/posix/handler.hpp>
#include <unistd.h>
#include <boost/process/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
struct close_fd_ : handler_base_ext
struct close_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
{
close_fd_(int fd) : fd_(fd) {}
@@ -27,12 +29,15 @@ struct close_fd_ : handler_base_ext
e.set_error(::boost::process::detail::get_last_error(), "close() failed");
}
int get_used_handles() {return fd_;}
private:
int fd_;
};
template <class Range>
struct close_fds_ : handler_base_ext
struct close_fds_ : handler_base_ext, ::boost::process::detail::uses_handles
{
public:
close_fds_(const Range &fds) : fds_(fds) {}
@@ -48,6 +53,8 @@ public:
}
}
Range& get_used_handles() {return fds_;}
private:
Range fds_;
};
@@ -55,7 +62,7 @@ private:
template <class FileDescriptor>
struct bind_fd_ : handler_base_ext
struct bind_fd_ : handler_base_ext, ::boost::process::detail::uses_handles
{
public:
bind_fd_(int id, const FileDescriptor &fd) : id_(id), fd_(fd) {}
@@ -67,6 +74,9 @@ public:
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
}
std::array<int, 2> get_used_handles() {return {id_, fd_};}
private:
int id_;
FileDescriptor fd_;

View File

@@ -13,16 +13,22 @@
#include <boost/process/pipe.hpp>
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <cstdio>
#include <unistd.h>
namespace boost { namespace process { namespace detail { namespace posix {
struct file_in : handler_base_ext
struct file_in : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor file;
int handle = file.handle();
std::array<int, 2> get_used_handles()
{
return {STDIN_FILENO, handle};
}
template<typename T>
file_in(T&& t) : file(std::forward<T>(t)) {}
file_in(FILE * f) : handle(fileno(f)) {}

View File

@@ -13,12 +13,13 @@
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <unistd.h>
namespace boost { namespace process { namespace detail { namespace posix {
template<int p1, int p2>
struct file_out : handler_base_ext
struct file_out : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor file;
int handle = file.handle();
@@ -27,6 +28,13 @@ struct file_out : handler_base_ext
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write), handle(file.handle()) {}
file_out(FILE * f) : handle(fileno(f)) {}
std::array<int, 3> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
return {handle, pp1, pp2};
}
template <typename Executor>
void on_exec_setup(Executor &e) const;

View File

@@ -0,0 +1,146 @@
// Copyright (c) 2019 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
#include <vector>
#include <system_error>
#include <dirent.h>
#include <sys/stat.h>
#include <algorithm>
#include <boost/process/detail/posix/handler.hpp>
namespace boost { namespace process { namespace detail { namespace posix {
using native_handle_type = int;
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
{
std::vector<native_handle_type> res;
std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
if (!dir)
{
ec = ::boost::process::detail::get_last_error();
return {};
}
else
ec.clear();
auto my_fd = ::dirfd(dir.get());
struct ::dirent * ent_p;
while ((ent_p = readdir(dir.get())) != nullptr)
{
if (ent_p->d_name[0] == '.')
continue;
const auto conv = std::atoi(ent_p->d_name);
if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
continue;
if (conv == my_fd)
continue;
res.push_back(conv);
}
return res;
}
inline std::vector<native_handle_type> get_handles()
{
std::error_code ec;
auto res = get_handles(ec);
if (ec)
boost::process::detail::throw_error(ec, "open_dir(\"/dev/fd\") failed");
return res;
}
inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
{
struct ::stat stat_;
if (::fstat(handle, &stat_) != 0)
{
ec = ::boost::process::detail::get_last_error();
}
else
ec.clear();
return S_ISCHR (stat_.st_mode) //This macro returns non-zero if the file is a character special file (a device like a terminal).
|| S_ISBLK (stat_.st_mode) // This macro returns non-zero if the file is a block special file (a device like a disk).
|| S_ISREG (stat_.st_mode) // This macro returns non-zero if the file is a regular file.
|| S_ISFIFO (stat_.st_mode) // This macro returns non-zero if the file is a FIFO special file, or a pipe. See section 15. Pipes and FIFOs.
|| S_ISSOCK (stat_.st_mode) ;// This macro returns non-zero if the file is a socket. See section 16. Sockets.;
}
inline bool is_stream_handle(native_handle_type handle)
{
std::error_code ec;
auto res = is_stream_handle(handle, ec);
if (ec)
boost::process::detail::throw_error(ec, "fstat() failed");
return res;
}
struct limit_handles_ : handler_base_ext
{
limit_handles_() {}
~limit_handles_() {}
mutable std::vector<int> used_handles;
template<typename Executor>
void on_setup(Executor & exec) const
{
used_handles = get_used_handles(exec);
}
template<typename Executor>
void on_exec_setup(Executor & exec) const
{
auto dir = ::opendir("/dev/fd");
if (!dir)
{
exec.set_error(::boost::process::detail::get_last_error(), "opendir(\"/dev/fd\")");
return;
}
auto my_fd = ::dirfd(dir);
struct ::dirent * ent_p;
while ((ent_p = readdir(dir)) != nullptr)
{
if (ent_p->d_name[0] == '.')
continue;
const auto conv = std::atoi(ent_p->d_name);
if ((conv == my_fd) || (conv == -1))
continue;
if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end())
continue;
if (::close(conv) != 0)
{
exec.set_error(::boost::process::detail::get_last_error(), "close() failed");
return;
}
}
::closedir(dir);
}
};
}}}}
#endif //PROCESS_HANDLES_HPP

View File

@@ -14,13 +14,21 @@
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <unistd.h>
#include <boost/process/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
struct null_in : handler_base_ext
struct null_in : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor source{"/dev/null", file_descriptor::read};
std::array<int, 2> get_used_handles()
{
return {STDIN_FILENO, source.handle()};
}
public:
template <class Executor>
void on_exec_setup(Executor &e) const

View File

@@ -13,17 +13,27 @@
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <unistd.h>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
template<int p1, int p2>
struct null_out : handler_base_ext
struct null_out : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor sink{"/dev/null", file_descriptor::write};
template <typename Executor>
void on_exec_setup(Executor &e) const;
std::array<int, 3> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
return {sink.handle(), pp1, pp2};
}
};
template<>

View File

@@ -13,17 +13,23 @@
#include <boost/process/pipe.hpp>
#include <boost/process/detail/posix/handler.hpp>
#include <unistd.h>
#include <boost/process/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
struct pipe_in : handler_base_ext
struct pipe_in : handler_base_ext, ::boost::process::detail::uses_handles
{
int source;
int sink; //opposite end
pipe_in(int sink, int source) : source(source), sink(sink) {}
std::array<int, 3> get_used_handles()
{
return {STDIN_FILENO, source, sink};
}
template<typename T>
pipe_in(T & p) : source(p.native_source()), sink(p.native_sink())
@@ -48,7 +54,9 @@ struct pipe_in : handler_base_ext
{
if (::dup2(source, STDIN_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
::close(source);
if (source != STDIN_FILENO)
::close(source);
::close(sink);
}

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, 0); //just to clean it up
::waitpid(p.pid, &status, WNOHANG); //just to clean it up
}
inline void terminate(const child_handle &p)

View File

@@ -54,11 +54,35 @@ inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
::sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
//I need to set the signal, because it might be ignore / default, in which case sigwait might not work.
using _signal_t = void(*)(int);
static thread_local _signal_t sigchld_handler = SIG_DFL;
struct signal_interceptor_t
{
static void handler_func(int val)
{
if ((sigchld_handler != SIG_DFL) && (sigchld_handler != SIG_IGN))
sigchld_handler(val);
}
signal_interceptor_t() { sigchld_handler = ::signal(SIGCHLD, &handler_func); }
~signal_interceptor_t() { ::signal(SIGCHLD, sigchld_handler); sigchld_handler = SIG_DFL;}
} signal_interceptor{};
if (sigemptyset(&sigset) != 0)
{
ec = get_last_error();
return false;
}
if (sigaddset(&sigset, SIGCHLD) != 0)
{
ec = get_last_error();
return false;
}
auto get_timespec =
[](const Duration & dur)
@@ -69,8 +93,8 @@ inline bool wait_until(
return ts;
};
pid_t ret;
int status;
int ret;
int status{0};
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
@@ -80,6 +104,7 @@ inline bool wait_until(
}
bool timed_out;
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
do
{
@@ -111,14 +136,18 @@ inline bool wait_until(
}
else if (timeout_pid == 0)
{
do
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
while (ts.tv_sec > 0 || ts.tv_nsec > 0)
{
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
::nanosleep(&ts, &rem);
if (::nanosleep(&ts, &rem) != 0)
{
auto err = errno;
if ((err == EINVAL) || (err == EFAULT))
break;
}
ts = get_timespec(time_out - Clock::now());
}
while (rem.tv_sec > 0 || rem.tv_nsec > 0);
::exit(0);
}
@@ -128,27 +157,29 @@ inline bool wait_until(
~child_cleaner_t()
{
int res;
::kill(pid, SIGTERM);
::waitpid(pid, &res, 0);
::kill(pid, SIGKILL);
::waitpid(pid, &res, WNOHANG);
}
};
child_cleaner_t child_cleaner{timeout_pid};
do
{
int ret_sig = 0;
int sig_{0};
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
&& (WIFEXITED(status) || WIFSIGNALED(status)))
ret_sig = ::sigwait(&sigset, nullptr);
&& (WIFEXITED(status) || WIFSIGNALED(status)))
return false;
ret = ::sigwait(&sigset, &sig_);
errno = 0;
ret = ::waitpid(p.pid, &status, WNOHANG);
if ((ret_sig == SIGCHLD) &&
if ((sig_ == SIGCHLD) &&
(old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
if (ret <= 0)
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0) // == > is running
{
timed_out = Clock::now() >= time_out;
if (timed_out)

View File

@@ -59,15 +59,14 @@ inline bool wait_until(
std::error_code & ec) noexcept
{
::sigset_t sigset;
::siginfo_t siginfo;
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
bool timed_out = false;
int ret;
auto get_timespec =
[](const Duration & dur)
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
auto get_timespec =
+[](const Duration & dur)
{
::timespec ts;
ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
@@ -75,9 +74,18 @@ inline bool wait_until(
return ts;
};
::sigset_t sigset;
bool timed_out = false;
int ret;
if (sigemptyset(&sigset) != 0)
{
ec = get_last_error();
return false;
}
if (sigaddset(&sigset, SIGCHLD) != 0)
{
ec = get_last_error();
return false;
}
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
@@ -86,7 +94,6 @@ inline bool wait_until(
return false;
}
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
do
{
auto ts = get_timespec(time_out - Clock::now());
@@ -98,71 +105,22 @@ inline bool wait_until(
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;
if ((errno == ECHILD) || (errno == ESRCH))
{
ec.clear();
return true;
}
else
{
ec = get_last_error();
return false;
}
}
//check if we're done
//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)
{
do
{
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
::nanosleep(&ts, &rem);
}
while (rem.tv_sec > 0 || rem.tv_nsec > 0);
::exit(0);
}
struct child_cleaner_t
{
pid_t pid;
~child_cleaner_t()
{
int res;
::kill(pid, SIGKILL);
::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;
}
//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)));
#endif
while (((ret != -1) || ((errno != ECHILD) && (errno != ESRCH))) && !(timed_out = (Clock::now() > time_out)));
if (errno != ECHILD)
{
@@ -175,6 +133,30 @@ inline bool wait_until(
return true; //even if timed out, there are no child proccessess left
}
#else
::timespec sleep_interval;
sleep_interval.tv_sec = 0;
sleep_interval.tv_nsec = 1000000;
while (!(timed_out = (Clock::now() > time_out)))
{
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED | WNOHANG);
if (ret == -1)
{
if ((errno == ECHILD) || (errno == ESRCH))
{
ec.clear();
return true;
}
ec = boost::process::detail::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);
}
return !timed_out;
#endif
}
template< class Clock, class Duration >

View File

@@ -0,0 +1,81 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_
#define BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_
#include <type_traits>
#include <boost/fusion/include/filter_if.hpp>
#include <boost/fusion/include/for_each.hpp>
#if defined(BOOST_POSIX_API)
#include <boost/process/detail/posix/handles.hpp>
#include <boost/process/detail/posix/asio_fwd.hpp>
#else
#include <boost/process/detail/windows/handles.hpp>
#include <boost/process/detail/windows/asio_fwd.hpp>
#endif
namespace boost { namespace process { namespace detail {
struct uses_handles
{
//If you get an error here, you must add a `get_handles` function that returns a range or a single handle value
void get_used_handles() const;
};
template<typename T>
struct does_use_handle: std::is_base_of<uses_handles, T> {};
template<typename T>
struct does_use_handle<T&> : std::is_base_of<uses_handles, T> {};
template<typename T>
struct does_use_handle<const T&> : std::is_base_of<uses_handles, T> {};
template<typename Char, typename Sequence>
class executor;
template<typename Func>
struct foreach_handle_invocator
{
Func & func;
foreach_handle_invocator(Func & func) : func(func) {}
template<typename Range>
void invoke(const Range & range) const
{
for (auto handle_ : range)
func(handle_);
}
void invoke(::boost::process::detail::api::native_handle_type handle) const {func(handle);};
template<typename T>
void operator()(T & val) const {invoke(val.get_used_handles());}
};
template<typename Executor, typename Function>
void foreach_used_handle(Executor &exec, Function &&func)
{
boost::fusion::for_each(boost::fusion::filter_if<does_use_handle<boost::mpl::_>>(exec.seq),
foreach_handle_invocator<Function>(func));
}
template<typename Executor>
std::vector<::boost::process::detail::api::native_handle_type>
get_used_handles(Executor &exec)
{
std::vector<::boost::process::detail::api::native_handle_type> res;
foreach_used_handle(exec, [&](::boost::process::detail::api::native_handle_type handle){res.push_back(handle);});
return res;
}
}}}
#endif /* BOOST_PROCESS_DETAIL_USED_HANDLES_HPP_ */

View File

@@ -17,19 +17,20 @@
#include <boost/asio/write.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/async_handler.hpp>
#include <boost/process/detail/windows/asio_fwd.hpp>
#include <boost/process/async_pipe.hpp>
#include <memory>
#include <future>
namespace boost { namespace process { namespace detail { namespace windows {
template<typename Buffer>
struct async_in_buffer : ::boost::process::detail::windows::handler_base_ext,
::boost::process::detail::windows::require_io_context
::boost::process::detail::windows::require_io_context,
::boost::process::detail::uses_handles
{
Buffer & buf;
@@ -42,6 +43,11 @@ struct async_in_buffer : ::boost::process::detail::windows::handler_base_ext,
std::shared_ptr<boost::process::async_pipe> pipe;
::boost::winapi::HANDLE_ get_used_handles() const
{
return std::move(*pipe).source().native_handle();
}
async_in_buffer(Buffer & buf) : buf(buf)
{
}

View File

@@ -16,6 +16,7 @@
#include <boost/winapi/error_codes.hpp>
#include <boost/asio/read.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/asio_fwd.hpp>
#include <istream>
@@ -108,12 +109,18 @@ struct async_out_buffer : ::boost::process::detail::windows::handler_base_ext,
template<int p1, int p2, typename Type>
struct async_out_future : ::boost::process::detail::windows::handler_base_ext,
::boost::process::detail::windows::require_io_context
::boost::process::detail::windows::require_io_context,
::boost::process::detail::uses_handles
{
std::shared_ptr<boost::process::async_pipe> pipe;
std::shared_ptr<std::promise<Type>> promise = std::make_shared<std::promise<Type>>();
std::shared_ptr<boost::asio::streambuf> buffer = std::make_shared<boost::asio::streambuf>();
::boost::winapi::HANDLE_ get_used_handles() const
{
return std::move(*pipe).sink().native_handle();
}
async_out_future(std::future<Type> & fut)
{

View File

@@ -40,17 +40,26 @@ 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;
inline async_pipe(boost::asio::io_context & ios,
const std::string & name = make_pipe_name())
: async_pipe(ios, ios, name) {}
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_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))
@@ -274,7 +283,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) : _source(ios_source), _sink(ios_sink)
const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink)
{
static constexpr int FILE_FLAG_OVERLAPPED_ = 0x40000000; //temporary
@@ -286,7 +295,7 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
#endif
::boost::winapi::PIPE_ACCESS_INBOUND_
| FILE_FLAG_OVERLAPPED_, //write flag
0, 1, 8192, 8192, 0, nullptr);
0, private_ ? 1 : ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
if (source == boost::winapi::INVALID_HANDLE_VALUE_)

View File

@@ -98,7 +98,7 @@ public:
return static_cast<int_type>(read_len);
}
bool is_open()
bool is_open() const
{
return (_source != ::boost::winapi::INVALID_HANDLE_VALUE_) ||
(_sink != ::boost::winapi::INVALID_HANDLE_VALUE_);
@@ -152,7 +152,7 @@ basic_pipe<Char, Traits>::basic_pipe(const std::string & name)
name_.c_str(),
::boost::winapi::PIPE_ACCESS_INBOUND_
| FILE_FLAG_OVERLAPPED_, //write flag
0, 1, 8192, 8192, 0, nullptr);
0, ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
if (source == boost::winapi::INVALID_HANDLE_VALUE_)
::boost::process::detail::throw_last_error("create_named_pipe() failed");

View File

@@ -13,16 +13,20 @@
#include <boost/winapi/process.hpp>
#include <boost/winapi/handles.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
#include <io.h>
namespace boost { namespace process { namespace detail { namespace windows {
struct file_in : public ::boost::process::detail::handler_base
struct file_in : public ::boost::process::detail::handler_base,
::boost::process::detail::uses_handles
{
file_descriptor file;
::boost::winapi::HANDLE_ handle = file.handle();
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
template<typename T>
file_in(T&& t) : file(std::forward<T>(t), file_descriptor::read) {}
file_in(FILE * f) : handle(reinterpret_cast<::boost::winapi::HANDLE_>(_get_osfhandle(_fileno(f)))) {}

View File

@@ -14,16 +14,21 @@
#include <boost/winapi/handles.hpp>
#include <boost/winapi/handle_info.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
template<int p1, int p2>
struct file_out : public ::boost::process::detail::handler_base
struct file_out : public ::boost::process::detail::handler_base,
::boost::process::detail::uses_handles
{
file_descriptor file;
::boost::winapi::HANDLE_ handle = file.handle();
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
template<typename T>
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write) {}
file_out(FILE * f) : handle(reinterpret_cast<void*>(_get_osfhandle(_fileno(f)))) {}

View File

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

View File

@@ -0,0 +1,262 @@
// Copyright (c) 2018 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLE_WORKAROUND_HPP_
#define BOOST_PROCESS_DETAIL_WINDOWS_HANDLE_WORKAROUND_HPP_
#include <boost/winapi/basic_types.hpp>
#include <boost/winapi/dll.hpp>
#include <boost/winapi/access_rights.hpp>
//#define BOOST_USE_WINDOWS_H 1
#if defined( BOOST_USE_WINDOWS_H )
#include <Winternl.h>
#endif
namespace boost { namespace process { namespace detail { namespace windows { namespace workaround
{
typedef struct _SYSTEM_HANDLE_ENTRY_
{
::boost::winapi::ULONG_ OwnerPid;
::boost::winapi::BYTE_ ObjectType;
::boost::winapi::BYTE_ HandleFlags;
::boost::winapi::USHORT_ HandleValue;
::boost::winapi::PVOID_ ObjectPointer;
::boost::winapi::ULONG_ AccessMask;
} SYSTEM_HANDLE_ENTRY_, *PSYSTEM_HANDLE_ENTRY_;
typedef struct _SYSTEM_HANDLE_INFORMATION_
{
::boost::winapi::ULONG_ Count;
SYSTEM_HANDLE_ENTRY_ Handle[1];
} SYSTEM_HANDLE_INFORMATION_, *PSYSTEM_HANDLE_INFORMATION_;
#if defined( BOOST_USE_WINDOWS_H )
using UNICODE_STRING_ = ::UNICODE_STRING;
using GENERIC_MAPPING_ = ::GENERIC_MAPPING;
using OBJECT_INFORMATION_CLASS_ = ::OBJECT_INFORMATION_CLASS;
constexpr static OBJECT_INFORMATION_CLASS_ ObjectTypeInformation = ::OBJECT_INFORMATION_CLASS::ObjectTypeInformation;
typedef struct _OBJECT_TYPE_INFORMATION_ {
UNICODE_STRING TypeName;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
UCHAR TypeIndex;
CHAR ReservedByte;
ULONG PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION_, *POBJECT_TYPE_INFORMATION_;
#else
typedef enum _OBJECT_INFORMATION_CLASS_
{
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllInformation,
ObjectDataInformation
} OBJECT_INFORMATION_CLASS_, *POBJECT_INFORMATION_CLASS_;
typedef struct _UNICODE_STRING_ {
::boost::winapi::USHORT_ Length;
::boost::winapi::USHORT_ MaximumLength;
::boost::winapi::LPWSTR_ Buffer;
} UNICODE_STRING_, *PUNICODE_STRING_;
typedef struct _GENERIC_MAPPING_ {
::boost::winapi::ACCESS_MASK_ GenericRead;
::boost::winapi::ACCESS_MASK_ GenericWrite;
::boost::winapi::ACCESS_MASK_ GenericExecute;
::boost::winapi::ACCESS_MASK_ GenericAll;
} GENERIC_MAPPING_;
#endif
typedef struct _OBJECT_BASIC_INFORMATION {
::boost::winapi::ULONG_ Attributes;
::boost::winapi::ACCESS_MASK_ GrantedAccess;
::boost::winapi::ULONG_ HandleCount;
::boost::winapi::ULONG_ PointerCount;
::boost::winapi::ULONG_ PagedPoolUsage;
::boost::winapi::ULONG_ NonPagedPoolUsage;
::boost::winapi::ULONG_ Reserved[3];
::boost::winapi::ULONG_ NameInformationLength;
::boost::winapi::ULONG_ TypeInformationLength;
::boost::winapi::ULONG_ SecurityDescriptorLength;
::boost::winapi::LARGE_INTEGER_ CreateTime;
} OBJECT_BASIC_INFORMATION_, *POBJECT_BASIC_INFORMATION_;
typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING_ Name;
} OBJECT_NAME_INFORMATION_, *POBJECT_NAME_INFORMATION_;
#if defined( BOOST_USE_WINDOWS_H )
extern "C"
{
using SYSTEM_INFORMATION_CLASS_ = ::SYSTEM_INFORMATION_CLASS;
constexpr static SYSTEM_INFORMATION_CLASS_ SystemHandleInformation_ = static_cast<SYSTEM_INFORMATION_CLASS_>(16);
inline ::boost::winapi::NTSTATUS_ nt_system_query_information(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
void * SystemInformation,
::boost::winapi::ULONG_ SystemInformationLength,
::boost::winapi::PULONG_ ReturnLength)
{
return ::NtQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
}
inline ::boost::winapi::NTSTATUS_ nt_query_object(
::boost::winapi::HANDLE_ Handle,
OBJECT_INFORMATION_CLASS_ ObjectInformationClass,
::boost::winapi::PVOID_ ObjectInformation,
::boost::winapi::ULONG_ ObjectInformationLength,
::boost::winapi::PULONG_ ReturnLength
)
{
return ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
}
}
#else
//this import workaround is to keep it a header-only library. and enums cannot be imported from the winapi.
extern "C"
{
typedef enum _SYSTEM_INFORMATION_CLASS_
{
SystemBasicInformation_ = 0,
SystemProcessorInformation_ = 1,
SystemPerformanceInformation_ = 2,
SystemTimeOfDayInformation_ = 3,
SystemProcessInformation_ = 5,
SystemProcessorPerformanceInformation_ = 8,
SystemHandleInformation_ = 16,
SystemPagefileInformation_ = 18,
SystemInterruptInformation_ = 23,
SystemExceptionInformation_ = 33,
SystemRegistryQuotaInformation_ = 37,
SystemLookasideInformation_ = 45
} SYSTEM_INFORMATION_CLASS_;
typedef struct _OBJECT_TYPE_INFORMATION_ {
UNICODE_STRING_ TypeName;
::boost::winapi::ULONG_ TotalNumberOfObjects;
::boost::winapi::ULONG_ TotalNumberOfHandles;
::boost::winapi::ULONG_ TotalPagedPoolUsage;
::boost::winapi::ULONG_ TotalNonPagedPoolUsage;
::boost::winapi::ULONG_ TotalNamePoolUsage;
::boost::winapi::ULONG_ TotalHandleTableUsage;
::boost::winapi::ULONG_ HighWaterNumberOfObjects;
::boost::winapi::ULONG_ HighWaterNumberOfHandles;
::boost::winapi::ULONG_ HighWaterPagedPoolUsage;
::boost::winapi::ULONG_ HighWaterNonPagedPoolUsage;
::boost::winapi::ULONG_ HighWaterNamePoolUsage;
::boost::winapi::ULONG_ HighWaterHandleTableUsage;
::boost::winapi::ULONG_ InvalidAttributes;
GENERIC_MAPPING_ GenericMapping;
::boost::winapi::ULONG_ ValidAccessMask;
::boost::winapi::BOOLEAN_ SecurityRequired;
::boost::winapi::BOOLEAN_ MaintainHandleCount;
::boost::winapi::UCHAR_ TypeIndex;
::boost::winapi::CHAR_ ReservedByte;
::boost::winapi::ULONG_ PoolType;
::boost::winapi::ULONG_ DefaultPagedPoolCharge;
::boost::winapi::ULONG_ DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION_, *POBJECT_TYPE_INFORMATION_;
/*
__kernel_entry NTSTATUS NtQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
);
*/
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_system_query_information_p )(
SYSTEM_INFORMATION_CLASS_,
void *,
::boost::winapi::ULONG_,
::boost::winapi::PULONG_);
/*
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject(
HANDLE Handle,
OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
*/
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_query_object_p )(
::boost::winapi::HANDLE_,
OBJECT_INFORMATION_CLASS_,
void *,
::boost::winapi::ULONG_,
::boost::winapi::PULONG_);
}
inline ::boost::winapi::NTSTATUS_ nt_system_query_information(
SYSTEM_INFORMATION_CLASS_ SystemInformationClass,
void *SystemInformation,
::boost::winapi::ULONG_ SystemInformationLength,
::boost::winapi::PULONG_ ReturnLength)
{
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Ntdll.dll");
static nt_system_query_information_p f = reinterpret_cast<nt_system_query_information_p>(::boost::winapi::get_proc_address(h, "NtQuerySystemInformation"));
return (*f)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
}
inline ::boost::winapi::BOOL_ nt_query_object(
::boost::winapi::HANDLE_ Handle,
OBJECT_INFORMATION_CLASS_ ObjectInformationClass,
void *ObjectInformation,
::boost::winapi::ULONG_ ObjectInformationLength,
::boost::winapi::PULONG_ ReturnLength)
{
static ::boost::winapi::HMODULE_ h = ::boost::winapi::get_module_handle(L"Ntdll.dll");
static nt_query_object_p f = reinterpret_cast<nt_query_object_p>(::boost::winapi::get_proc_address(h, "NtQueryObject"));
return (*f)(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
}
#endif
}}}}}
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_JOB_WORKAROUND_HPP_ */

View File

@@ -0,0 +1,176 @@
// Copyright (c) 2019 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
#define BOOST_PROCESS_DETAIL_WINDOWS_HANDLES_HPP_
#include <vector>
#include <system_error>
#include <boost/process/detail/windows/handle_workaround.hpp>
#include <boost/process/detail/windows/handler.hpp>
#include <boost/winapi/get_current_process_id.hpp>
namespace boost { namespace process { namespace detail {
template<typename Executor, typename Function>
void foreach_used_handle(Executor &exec, Function &&func);
namespace windows {
using native_handle_type = ::boost::winapi::HANDLE_ ;
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
{
auto pid = ::boost::winapi::GetCurrentProcessId();
std::vector<char> buffer(2048);
constexpr static auto STATUS_INFO_LENGTH_MISMATCH_ = static_cast<::boost::winapi::NTSTATUS_>(0xC0000004l);
auto info_pointer = reinterpret_cast<workaround::SYSTEM_HANDLE_INFORMATION_*>(buffer.data());
::boost::winapi::NTSTATUS_ nt_status = STATUS_INFO_LENGTH_MISMATCH_;
for (int cnt = 0;
nt_status == STATUS_INFO_LENGTH_MISMATCH_;
nt_status = workaround::nt_system_query_information(
workaround::SystemHandleInformation_,
info_pointer, buffer.size(),
NULL))
{
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,14 +14,18 @@
#include <boost/winapi/handles.hpp>
#include <boost/winapi/handle_info.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
struct null_in : public ::boost::process::detail::handler_base
struct null_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
file_descriptor source{"NUL", file_descriptor::read};
::boost::winapi::HANDLE_ get_used_handles() const { return source.handle(); }
public:
template <class WindowsExecutor>
void on_setup(WindowsExecutor &e) const

View File

@@ -14,15 +14,18 @@
#include <boost/winapi/handles.hpp>
#include <boost/winapi/handle_info.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
template<int p1, int p2>
struct null_out : public ::boost::process::detail::handler_base
struct null_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
file_descriptor sink {"NUL", file_descriptor::write}; //works because it gets destroyed AFTER launch.
::boost::winapi::HANDLE_ get_used_handles() const { return sink.handle(); }
template <typename WindowsExecutor>
void on_setup(WindowsExecutor &e) const;
};

View File

@@ -12,14 +12,17 @@
#include <boost/winapi/process.hpp>
#include <boost/winapi/handles.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/handler_base.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
struct pipe_in : public ::boost::process::detail::handler_base
struct pipe_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
::boost::winapi::HANDLE_ handle;
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
pipe_in(::boost::winapi::HANDLE_ handle) : handle(handle) {}
template<typename T> //async_pipe

View File

@@ -13,6 +13,7 @@
#include <boost/winapi/process.hpp>
#include <boost/winapi/handles.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/handler_base.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
@@ -20,10 +21,12 @@ namespace boost { namespace process { namespace detail { namespace windows {
template<int p1, int p2>
struct pipe_out : public ::boost::process::detail::handler_base
struct pipe_out : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
::boost::winapi::HANDLE_ handle;
::boost::winapi::HANDLE_ get_used_handles() const { return handle; }
pipe_out(::boost::winapi::HANDLE_ handle) : handle(handle) {}
template<typename T>
pipe_out(T & p) : handle(p.native_sink())

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

View File

@@ -7,6 +7,7 @@
#define BOOST_PROCESS_EXTENSIONS_HPP_
#include <boost/process/detail/handler.hpp>
#include <boost/process/detail/used_handles.hpp>
#if defined(BOOST_WINDOWS_API)
#include <boost/process/detail/windows/executor.hpp>
@@ -62,6 +63,9 @@ using ::boost::process::detail::api::async_handler;
using ::boost::process::detail::get_io_context;
using ::boost::process::detail::get_last_error;
using ::boost::process::detail::throw_last_error;
using ::boost::process::detail::uses_handles;
using ::boost::process::detail::foreach_used_handle;
using ::boost::process::detail::get_used_handles;
///This handler is invoked before the process in launched, to setup parameters. The required signature is `void(Exec &)`, where `Exec` is a template parameter.
constexpr boost::process::detail::make_handler_t<boost::process::detail::on_setup_> on_setup;

View File

@@ -0,0 +1,107 @@
// Copyright (c) 2019 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROCESS_HANDLES_HPP_
#define BOOST_PROCESS_HANDLES_HPP_
/**
* \file boost/process/handles.hpp
*
* Defines functions to obtain handles of the current process and limit the amount for inherited ones.
*/
#include <boost/process/detail/config.hpp>
#if defined(BOOST_POSIX_API)
#include <boost/process/detail/posix/handles.hpp>
#elif defined(BOOST_WINDOWS_API)
#include <boost/process/detail/windows/handles.hpp>
#endif
#include <boost/process/detail/used_handles.hpp>
namespace boost { namespace this_process
{
///The native type for handles
using native_handle_type = ::boost::process::detail::api::native_handle_type;
/**
* Get a snapshot of all handles of the process (i.e. file descriptors on posix and handles on windows) of the current process.
*
* \note This function might not work on certain posix systems.
*
* \note On Windows version older than windows 8 this function will iterate all the system handles, meaning it might be quite slow.
*
* \warning This functionality is utterly prone to race conditions, since other threads might open or close handles.
*
* \return The list of all open handles of the current process
*/
inline std::vector<native_handle_type> get_handles()
{
return ::boost::process::detail::api::get_handles();
}
/** \overload std::vector<native_handle_type> get_handles() */
inline std::vector<native_handle_type> get_handles(std::error_code &ec)
{
return ::boost::process::detail::api::get_handles(ec);
}
/** Determines if a given handle is a a stream-handle, i.e. any handle that can be used with read and write functions.
* Stream handles include pipes, regular files and sockets.
*
* \return Indicates if it's a stream handle.
*/
inline bool is_stream_handle(native_handle_type handle)
{
return ::boost::process::detail::api::is_stream_handle(handle);
}
/** \overload bool is_stream_handle(native_handle_type handle) */
inline bool is_stream_handle(native_handle_type handle, std::error_code &ec)
{
return ::boost::process::detail::api::is_stream_handle(handle, ec);
}
}
namespace process
{
namespace detail
{
using limit_handles_ = ::boost::process::detail::api::limit_handles_;
}
/**
* The limit_handles property sets all properties to be inherited only expcitly. It closes all unused file-descriptors on posix after the fork and
* removes the inherit flags on windows.
*
* \note This is executed after the fork on posix.
*
* \code{.cpp}
* system("gcc", limit_handles);
* \endcode
*
* Since limit also closes the standard handles unless they are explicitly redirected they can be ignored by `limit_handles` in the following way.
*
* \code{.cpp}
* system("gcc", limit_handles.allowStd())
* \endcode
*
*/
const static ::boost::process::detail::api::limit_handles_ limit_handles;
}
}
#endif //BOOST_PROCESS_HANDLES_HPP_

View File

@@ -120,6 +120,13 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
///Move Constructor
basic_pipebuf(basic_pipebuf && ) = default;
///Destructor -> writes the frest of the data
~basic_pipebuf()
{
if (is_open())
overflow(Traits::eof());
}
///Move construct from a pipe.
basic_pipebuf(pipe_type && p) : _pipe(std::move(p)),
_write(default_buffer_size),
@@ -155,7 +162,7 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
///Writes characters to the associated output sequence from the put area
int_type overflow(int_type ch = traits_type::eof()) override
{
if ((ch != traits_type::eof()) && _pipe.is_open())
if (_pipe.is_open() && (ch != traits_type::eof()))
{
if (this->pptr() == this->epptr())
{
@@ -173,6 +180,9 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
return ch;
}
}
else if (ch == traits_type::eof())
this->sync();
return traits_type::eof();
}
///Synchronizes the buffers with the associated character sequence
@@ -212,6 +222,36 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
const pipe_type &pipe() const & {return _pipe;}
///Get a rvalue reference to the pipe. Qualified as rvalue.
pipe_type && pipe() && {return std::move(_pipe);}
///Check if the pipe is open
bool is_open() const {return _pipe.is_open(); }
///Open a new pipe
basic_pipebuf<CharT, Traits>* open()
{
if (is_open())
return nullptr;
_pipe = pipe();
return this;
}
///Open a new named pipe
basic_pipebuf<CharT, Traits>* open(const std::string & name)
{
if (is_open())
return nullptr;
_pipe = pipe(name);
return this;
}
///Flush the buffer & close the pipe
basic_pipebuf<CharT, Traits>* close()
{
if (!is_open())
return nullptr;
overflow(Traits::eof());
return this;
}
private:
pipe_type _pipe;
std::vector<char_type> _write;
@@ -223,8 +263,13 @@ private:
return false;
auto base = this->pbase();
if (base == this->pptr())
return true;
std::ptrdiff_t wrt = _pipe.write(base,
static_cast<typename pipe_type::int_type>(this->pptr() - base));
std::ptrdiff_t diff = this->pptr() - base;
if (wrt < diff)
@@ -320,6 +365,33 @@ public:
const pipe_type &pipe() const & {return _buf.pipe();}
///Get a rvalue reference to the pipe. Qualified as rvalue.
pipe_type && pipe() && {return std::move(_buf).pipe();}
///Check if the pipe is open
bool is_open() const {return _buf->is_open();}
///Open a new pipe
void open()
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Open a new named pipe
void open(const std::string & name)
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Flush the buffer & close the pipe
void close()
{
if (_buf.close() == nullptr)
this->setstate(std::ios_base::failbit);
}
};
typedef basic_ipstream<char> ipstream;
@@ -402,6 +474,31 @@ public:
const pipe_type &pipe() const & {return _buf.pipe();}
///Get a rvalue reference to the pipe. Qualified as rvalue.
pipe_type && pipe() && {return std::move(_buf).pipe();}
///Open a new pipe
void open()
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Open a new named pipe
void open(const std::string & name)
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Flush the buffer & close the pipe
void close()
{
if (_buf.close() == nullptr)
this->setstate(std::ios_base::failbit);
}
};
typedef basic_opstream<char> opstream;
@@ -484,6 +581,31 @@ public:
const pipe_type &pipe() const & {return _buf.pipe();}
///Get a rvalue reference to the pipe. Qualified as rvalue.
pipe_type && pipe() && {return std::move(_buf).pipe();}
///Open a new pipe
void open()
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Open a new named pipe
void open(const std::string & name)
{
if (_buf.open() == nullptr)
this->setstate(std::ios_base::failbit);
else
this->clear();
}
///Flush the buffer & close the pipe
void close()
{
if (_buf.close() == nullptr)
this->setstate(std::ios_base::failbit);
}
};
typedef basic_pstream<char> pstream;

View File

@@ -16,6 +16,7 @@ if [ os.name ] = NT
lib ws2_32 ;
lib shell32 ;
lib Advapi32 ;
lib Ntdll ;
}
project : requirements
@@ -44,7 +45,7 @@ alias coroutine : /boost//coroutine : <link>static ;
lib multi_ref : multi_ref1.cpp multi_ref2.cpp system : <target-os>windows:<source>shell32 ;
exe sparring_partner : sparring_partner.cpp program_options system filesystem iostreams :
<warnings>off <target-os>windows:<source>shell32
<warnings>off <target-os>windows:<source>shell32 <target-os>windows:<source>Ntdll
;
exe exit_argc : exit_argc.cpp :
@@ -55,8 +56,15 @@ exe sub_launch : sub_launcher.cpp program_options iostreams system filesystem :
rule test-options ( name )
{
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
#return --log_level=error --report_level=detailed -- ;
if "--boost-process-report-ci" in [ modules.peek : ARGV ]
{
return --log_sink=log_$(name).xml --log_format=XML --log_level=error --report_sink=report_$(name).xml --report_format=XML --report_level=detailed -- ;
}
else
{
return --log_level=error --report_level=detailed -- ;
}
}
@@ -93,6 +101,7 @@ test-suite with-valgrind :
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch ]
[ run group.cpp system thread filesystem : [ test-options group ] : sub_launch : <build>no <target-os>windows:<build>yes <define>BOOST_USE_WINDOWS_H=1 : group-windows-h ]
[ run group_wait.cpp system thread filesystem : [ test-options group_wait ] : sparring_partner ]
[ run limit_fd.cpp program_options system filesystem : [ test-options limit_fd ] : sparring_partner ]
[ run run_exe.cpp filesystem : : sparring_partner ]
[ run run_exe_path.cpp filesystem : [ test-options run_exe_path ] : sparring_partner ]
[ run search_path.cpp filesystem system : [ test-options search_path ] : : <target-os>windows:<source>shell32 ]

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

View File

@@ -58,10 +58,15 @@ BOOST_AUTO_TEST_CASE(group_test, *boost::unit_test::timeout(5))
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cout << "group_test out" << std::endl;
}
BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
{
std::cout << "attached" << std::endl;
using boost::unit_test::framework::master_test_suite;
bp::ipstream is;
@@ -89,10 +94,11 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
BOOST_REQUIRE(sub_c);
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
#if defined( BOOST_POSIX_API )
::waitpid(sub_c.id(), nullptr, WNOHANG);
BOOST_CHECK(kill(sub_c.id(), 0) == 0);
#else
BOOST_CHECK(sub_c.running());
@@ -101,23 +107,27 @@ BOOST_AUTO_TEST_CASE(attached, *boost::unit_test::timeout(5))
BOOST_REQUIRE_NO_THROW(g.terminate());
BOOST_CHECK(sub_c);
std::this_thread::sleep_for(std::chrono::milliseconds(50)); //just to be sure.
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //just to be sure.
BOOST_CHECK(!c.running());
#if defined( BOOST_POSIX_API )
bool still_runs = kill(sub_c.id(), 0) == 0;
#else
errno = 0;
::waitpid(sub_c.id(), nullptr, WNOHANG);
bool still_runs = (kill(sub_c.id(), 0) == 0) && (errno != ECHILD) && (errno != ESRCH);
#else
bool still_runs = sub_c.running();
#endif
BOOST_CHECK_MESSAGE(!still_runs, boost::process::detail::get_last_error().message());
BOOST_CHECK(!still_runs);
if (still_runs)
sub_c.terminate();
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cout << "attached out" << std::endl;
}
@@ -176,4 +186,6 @@ BOOST_AUTO_TEST_CASE(detached, *boost::unit_test::timeout(5))
BOOST_CHECK(!c.running());
if (c.running())
c.terminate();
std::cerr << "detached out" << std::endl;
}

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

179
test/limit_fd.cpp Normal file
View File

@@ -0,0 +1,179 @@
// 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(), 3);
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() + 2, 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()), 1);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1);
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()), 0);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0);
#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);
std::cout << "Pushing " << handle << std::endl;
});
}
};
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, 42);
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0);
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0);
}
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(stdout), 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(stdout), 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

@@ -235,4 +235,37 @@ 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();

View File

@@ -17,6 +17,7 @@
#include <boost/range/algorithm_ext/push_back.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/process/environment.hpp>
#include <boost/process/handles.hpp>
#include <vector>
#include <string>
#include <iterator>
@@ -57,6 +58,7 @@ int main(int argc, char *argv[])
("pwd", bool_switch())
("query", value<std::string>())
("stdin-to-stdout", bool_switch())
("has-handle", value<std::uintptr_t>())
#if defined(BOOST_POSIX_API)
("posix-echo-one", value<std::vector<std::string> >()->multitoken())
("posix-echo-two", value<std::vector<std::string> >()->multitoken());
@@ -223,5 +225,15 @@ int main(int argc, char *argv[])
std::cout << si.dwFlags << std::endl;
}
#endif
else if (vm.count("has-handle"))
{
#if defined(BOOST_WINDOWS_API)
const auto handle = reinterpret_cast<boost::this_process::native_handle_type>(vm["has-handle"].as<std::uintptr_t>());
#else
const auto handle = static_cast<boost::this_process::native_handle_type>(vm["has-handle"].as<std::uintptr_t>());
#endif
auto all_handles = boost::this_process::get_handles();
return (std::find(all_handles.begin(), all_handles.end(), handle) != all_handles.end()) ? EXIT_SUCCESS : EXIT_FAILURE;
}
return EXIT_SUCCESS;
}