summaryrefslogtreecommitdiff
path: root/boost/process
diff options
context:
space:
mode:
Diffstat (limited to 'boost/process')
-rw-r--r--boost/process/detail/child_decl.hpp15
-rw-r--r--boost/process/detail/posix/async_in.hpp18
-rw-r--r--boost/process/detail/posix/async_out.hpp16
-rw-r--r--boost/process/detail/posix/basic_pipe.hpp2
-rw-r--r--boost/process/detail/posix/close_in.hpp6
-rw-r--r--boost/process/detail/posix/close_out.hpp5
-rw-r--r--boost/process/detail/posix/executor.hpp195
-rw-r--r--boost/process/detail/posix/fd.hpp16
-rw-r--r--boost/process/detail/posix/file_in.hpp8
-rw-r--r--boost/process/detail/posix/file_out.hpp12
-rw-r--r--boost/process/detail/posix/handles.hpp146
-rw-r--r--boost/process/detail/posix/null_in.hpp10
-rw-r--r--boost/process/detail/posix/null_out.hpp14
-rw-r--r--boost/process/detail/posix/pipe_in.hpp14
-rw-r--r--boost/process/detail/posix/pipe_out.hpp14
-rw-r--r--boost/process/detail/posix/terminate.hpp2
-rw-r--r--boost/process/detail/posix/wait_for_exit.hpp66
-rw-r--r--boost/process/detail/posix/wait_group.hpp115
-rw-r--r--boost/process/detail/used_handles.hpp81
-rw-r--r--boost/process/detail/windows/async_in.hpp10
-rw-r--r--boost/process/detail/windows/async_out.hpp9
-rw-r--r--boost/process/detail/windows/async_pipe.hpp105
-rw-r--r--boost/process/detail/windows/basic_pipe.hpp4
-rw-r--r--boost/process/detail/windows/file_in.hpp6
-rw-r--r--boost/process/detail/windows/file_out.hpp7
-rw-r--r--boost/process/detail/windows/group_ref.hpp7
-rw-r--r--boost/process/detail/windows/handle_workaround.hpp262
-rw-r--r--boost/process/detail/windows/handles.hpp176
-rw-r--r--boost/process/detail/windows/null_in.hpp6
-rw-r--r--boost/process/detail/windows/null_out.hpp5
-rw-r--r--boost/process/detail/windows/pipe_in.hpp5
-rw-r--r--boost/process/detail/windows/pipe_out.hpp5
-rw-r--r--boost/process/detail/windows/wait_group.hpp6
-rw-r--r--boost/process/extend.hpp4
-rw-r--r--boost/process/handles.hpp107
-rw-r--r--boost/process/pipe.hpp124
36 files changed, 1351 insertions, 252 deletions
diff --git a/boost/process/detail/child_decl.hpp b/boost/process/detail/child_decl.hpp
index c30a7aa6f1..6b782140b1 100644
--- a/boost/process/detail/child_decl.hpp
+++ b/boost/process/detail/child_decl.hpp
@@ -145,11 +145,12 @@ public:
bool running(std::error_code & ec) noexcept
{
- if (valid() && !_exited())
+ ec.clear();
+ if (valid() && !_exited() && !ec)
{
int exit_code = 0;
auto res = boost::process::detail::api::is_running(_child_handle, exit_code, ec);
- if (!res && !_exited())
+ if (!ec && !res && !_exited())
_exit_status->store(exit_code);
return res;
@@ -159,10 +160,11 @@ public:
void terminate(std::error_code & ec) noexcept
{
- if (valid() && running(ec))
+ if (valid() && running(ec) && !ec)
boost::process::detail::api::terminate(_child_handle, ec);
- _terminated = true;
+ if (!ec)
+ _terminated = true;
}
void wait(std::error_code & ec) noexcept
@@ -171,7 +173,8 @@ public:
{
int exit_code = 0;
boost::process::detail::api::wait(_child_handle, exit_code, ec);
- _exit_status->store(exit_code);
+ if (!ec)
+ _exit_status->store(exit_code);
}
}
@@ -188,7 +191,7 @@ public:
{
int exit_code = 0;
auto b = boost::process::detail::api::wait_until(_child_handle, exit_code, timeout_time, ec);
- if (!b)
+ if (!b || ec)
return false;
_exit_status->store(exit_code);
}
diff --git a/boost/process/detail/posix/async_in.hpp b/boost/process/detail/posix/async_in.hpp
index 5033d3e6f8..513163816d 100644
--- a/boost/process/detail/posix/async_in.hpp
+++ b/boost/process/detail/posix/async_in.hpp
@@ -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)
{
diff --git a/boost/process/detail/posix/async_out.hpp b/boost/process/detail/posix/async_out.hpp
index 983779ec26..bbf4179081 100644
--- a/boost/process/detail/posix/async_out.hpp
+++ b/boost/process/detail/posix/async_out.hpp
@@ -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)
{
diff --git a/boost/process/detail/posix/basic_pipe.hpp b/boost/process/detail/posix/basic_pipe.hpp
index 14d140f1cc..92db99d982 100644
--- a/boost/process/detail/posix/basic_pipe.hpp
+++ b/boost/process/detail/posix/basic_pipe.hpp
@@ -97,7 +97,7 @@ public:
return read_len;
}
- bool is_open()
+ bool is_open() const
{
return (_source != -1) ||
(_sink != -1);
diff --git a/boost/process/detail/posix/close_in.hpp b/boost/process/detail/posix/close_in.hpp
index 74cffd6729..708f1ac44b 100644
--- a/boost/process/detail/posix/close_in.hpp
+++ b/boost/process/detail/posix/close_in.hpp
@@ -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;}
+
};
}}}}
diff --git a/boost/process/detail/posix/close_out.hpp b/boost/process/detail/posix/close_out.hpp
index f3659f5544..83d46327c7 100644
--- a/boost/process/detail/posix/close_out.hpp
+++ b/boost/process/detail/posix/close_out.hpp
@@ -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<>
diff --git a/boost/process/detail/posix/executor.hpp b/boost/process/detail/posix/executor.hpp
index 1390a58ee6..c031689b98 100644
--- a/boost/process/detail/posix/executor.hpp
+++ b/boost/process/detail/posix/executor.hpp
@@ -22,47 +22,12 @@
#include <errno.h>
#include <unistd.h>
-#if !defined(__GLIBC__)
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
-#endif
namespace boost { namespace process { namespace detail { namespace posix {
-inline int execvpe(const char* filename, char * const arg_list[], char* env[])
-{
-#if defined(__GLIBC__)
- return ::execvpe(filename, arg_list, env);
-#else
- //use my own implementation
- std::string fn = filename;
- if ((fn.find('/') == std::string::npos) && ::access(fn.c_str(), X_OK))
- {
- auto e = ::environ;
- while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
- e++;
-
- if (e != nullptr)
- {
- std::vector<std::string> path;
- boost::split(path, *e, boost::is_any_of(":"));
-
- for (const std::string & pp : path)
- {
- auto p = pp + "/" + filename;
- if (!::access(p.c_str(), X_OK))
- {
- fn = p;
- break;
- }
- }
- }
- }
- return ::execve(fn.c_str(), arg_list, env);
-#endif
-}
-
template<typename Executor>
struct on_setup_t
{
@@ -295,15 +260,41 @@ class executor
return;
//EAGAIN not yet forked, EINTR interrupted, i.e. try again
else if ((err != EAGAIN ) && (err != EINTR))
- {
- ::close(source);
set_error(std::error_code(err, std::system_category()), "Error read pipe");
- }
}
- ::close(source);
set_error(ec, std::move(msg));
}
+ std::string prepare_cmd_style_fn; //buffer
+
+ inline void prepare_cmd_style() //this does what execvpe does - but we execute it in the father process, to avoid allocations.
+ {
+ //use my own implementation
+ prepare_cmd_style_fn = exe;
+ if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK))
+ {
+ auto e = ::environ;
+ while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
+ e++;
+
+ if (e != nullptr)
+ {
+ std::vector<std::string> path;
+ boost::split(path, *e, boost::is_any_of(":"));
+
+ for (const std::string & pp : path)
+ {
+ auto p = pp + "/" + exe;
+ if (!::access(p.c_str(), X_OK))
+ {
+ prepare_cmd_style_fn = p;
+ break;
+ }
+ }
+ }
+ }
+ exe = prepare_cmd_style_fn.c_str();
+ }
std::error_code _ec;
std::string _msg;
@@ -342,6 +333,8 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
boost::fusion::for_each(seq, call_on_setup(*this));
if (_ec)
return child();
+ if (cmd_style)
+ prepare_cmd_style();
this->pid = ::fork();
if (pid == -1)
@@ -353,10 +346,7 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
else if (pid == 0)
{
boost::fusion::for_each(seq, call_on_exec_setup(*this));
- if (cmd_style)
- ::boost::process::detail::posix::execvpe(exe, cmd_line, env);
- else
- ::execve(exe, cmd_line, env);
+ ::execve(exe, cmd_line, env);
auto ec = boost::process::detail::get_last_error();
boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
_exit(EXIT_FAILURE);
@@ -372,71 +362,85 @@ child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore
template<typename Sequence>
child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
{
- int p[2];
- if (::pipe(p) == -1)
- {
- set_error(::boost::process::detail::get_last_error(), "pipe(2) failed");
- return child();
- }
- if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
{
- auto err = ::boost::process::detail::get_last_error();
- ::close(p[0]);
- ::close(p[1]);
- set_error(err, "fcntl(2) failed");
- return child();
- }
- _ec.clear();
- boost::fusion::for_each(seq, call_on_setup(*this));
+ struct pipe_guard
+ {
+ int p[2];
+ pipe_guard() : p{-1,-1} {}
- if (_ec)
- {
- boost::fusion::for_each(seq, call_on_error(*this, _ec));
- return child();
- }
+ ~pipe_guard()
+ {
+ if (p[0] != -1)
+ ::close(p[0]);
+ if (p[1] != -1)
+ ::close(p[1]);
+ }
+ } p{};
- this->pid = ::fork();
- if (pid == -1)
- {
- _ec = boost::process::detail::get_last_error();
- _msg = "fork() failed";
- boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
- boost::fusion::for_each(seq, call_on_error(*this, _ec));
+ if (::pipe(p.p) == -1)
+ {
+ set_error(::boost::process::detail::get_last_error(), "pipe(2) failed");
+ return child();
+ }
+ if (::fcntl(p.p[1], F_SETFD, FD_CLOEXEC) == -1)
+ {
+ auto err = ::boost::process::detail::get_last_error();
+ set_error(err, "fcntl(2) failed");//this might throw, so we need to be sure our pipe is safe.
+ return child();
+ }
+ _ec.clear();
+ boost::fusion::for_each(seq, call_on_setup(*this));
- return child();
- }
- else if (pid == 0)
- {
- _pipe_sink = p[1];
- ::close(p[0]);
+ if (_ec)
+ {
+ boost::fusion::for_each(seq, call_on_error(*this, _ec));
+ return child();
+ }
- boost::fusion::for_each(seq, call_on_exec_setup(*this));
if (cmd_style)
- ::boost::process::detail::posix::execvpe(exe, cmd_line, env);
- else
- ::execve(exe, cmd_line, env);
- _ec = boost::process::detail::get_last_error();
- _msg = "execve failed";
- boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
+ prepare_cmd_style();
- _write_error(p[1]);
+ this->pid = ::fork();
+ if (pid == -1)
+ {
+ _ec = boost::process::detail::get_last_error();
+ _msg = "fork() failed";
+ boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
+ boost::fusion::for_each(seq, call_on_error(*this, _ec));
+ return child();
+ }
+ else if (pid == 0)
+ {
+ _pipe_sink = p.p[1];
+ ::close(p.p[0]);
- _exit(EXIT_FAILURE);
- return child();
- }
+ boost::fusion::for_each(seq, call_on_exec_setup(*this));
+ ::execve(exe, cmd_line, env);
+ _ec = boost::process::detail::get_last_error();
+ _msg = "execve failed";
+ boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
- child c(child_handle(pid), exit_status);
+ _write_error(_pipe_sink);
+ ::close(p.p[1]);
+
+ _exit(EXIT_FAILURE);
+ return child();
+ }
- ::close(p[1]);
- _read_error(p[0]);
+ ::close(p.p[1]);
+ p.p[1] = -1;
+ _read_error(p.p[0]);
+ }
if (_ec)
{
boost::fusion::for_each(seq, call_on_error(*this, _ec));
return child();
}
- else
- boost::fusion::for_each(seq, call_on_success(*this));
+
+ child c(child_handle(pid), exit_status);
+
+ boost::fusion::for_each(seq, call_on_success(*this));
if (_ec)
{
@@ -489,6 +493,8 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
return child();
}
_ec.clear();
+ if (cmd_style)
+ this->prepare_cmd_style();
this->pid = ::vfork();
if (pid == -1)
@@ -504,10 +510,7 @@ child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
{
boost::fusion::for_each(seq, call_on_exec_setup(*this));
- if (cmd_style)
- ::boost::process::detail::posix::execvpe(exe, cmd_line, env);
- else
- ::execve(exe, cmd_line, env);
+ ::execve(exe, cmd_line, env);
_ec = boost::process::detail::get_last_error();
_msg = "execve failed";
diff --git a/boost/process/detail/posix/fd.hpp b/boost/process/detail/posix/fd.hpp
index 51790c3237..bd5f153d37 100644
--- a/boost/process/detail/posix/fd.hpp
+++ b/boost/process/detail/posix/fd.hpp
@@ -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_;
diff --git a/boost/process/detail/posix/file_in.hpp b/boost/process/detail/posix/file_in.hpp
index 7ddd71684b..799f92c3e4 100644
--- a/boost/process/detail/posix/file_in.hpp
+++ b/boost/process/detail/posix/file_in.hpp
@@ -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)) {}
diff --git a/boost/process/detail/posix/file_out.hpp b/boost/process/detail/posix/file_out.hpp
index 8e3d4eb716..ce32b32635 100644
--- a/boost/process/detail/posix/file_out.hpp
+++ b/boost/process/detail/posix/file_out.hpp
@@ -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;
diff --git a/boost/process/detail/posix/handles.hpp b/boost/process/detail/posix/handles.hpp
new file mode 100644
index 0000000000..1572f05933
--- /dev/null
+++ b/boost/process/detail/posix/handles.hpp
@@ -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
diff --git a/boost/process/detail/posix/null_in.hpp b/boost/process/detail/posix/null_in.hpp
index 33ced810b1..cf64a1dec3 100644
--- a/boost/process/detail/posix/null_in.hpp
+++ b/boost/process/detail/posix/null_in.hpp
@@ -14,13 +14,21 @@
#include <boost/process/detail/posix/handler.hpp>
#include <boost/process/detail/posix/file_descriptor.hpp>
#include <unistd.h>
+#include <boost/process/detail/used_handles.hpp>
+#include <array>
namespace boost { namespace process { namespace detail { namespace posix {
-struct null_in : handler_base_ext
+struct null_in : handler_base_ext, ::boost::process::detail::uses_handles
{
file_descriptor source{"/dev/null", file_descriptor::read};
+ std::array<int, 2> get_used_handles()
+ {
+ return {STDIN_FILENO, source.handle()};
+ }
+
+
public:
template <class Executor>
void on_exec_setup(Executor &e) const
diff --git a/boost/process/detail/posix/null_out.hpp b/boost/process/detail/posix/null_out.hpp
index 3874475f4d..b879ff54de 100644
--- a/boost/process/detail/posix/null_out.hpp
+++ b/boost/process/detail/posix/null_out.hpp
@@ -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<>
diff --git a/boost/process/detail/posix/pipe_in.hpp b/boost/process/detail/posix/pipe_in.hpp
index 18ca856573..da8b3838be 100644
--- a/boost/process/detail/posix/pipe_in.hpp
+++ b/boost/process/detail/posix/pipe_in.hpp
@@ -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);
}
diff --git a/boost/process/detail/posix/pipe_out.hpp b/boost/process/detail/posix/pipe_out.hpp
index 9f7da9464b..d54cca4efa 100644
--- a/boost/process/detail/posix/pipe_out.hpp
+++ b/boost/process/detail/posix/pipe_out.hpp
@@ -52,8 +52,10 @@ template<typename Executor>
void pipe_out<1,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDOUT_FILENO) == -1)
- e.set_error(::boost::process::detail::get_last_error(), "dup3() failed");
- ::close(sink);
+ e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
+
+ if (sink != STDOUT_FILENO)
+ ::close(sink);
::close(source);
}
@@ -63,7 +65,9 @@ void pipe_out<2,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDERR_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
- ::close(sink);
+
+ if (sink != STDOUT_FILENO)
+ ::close(sink);
::close(source);
}
@@ -75,8 +79,8 @@ void pipe_out<1,2>::on_exec_setup(Executor &e) const
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
if (::dup2(sink, STDERR_FILENO) == -1)
e.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
- ::close(sink);
- ::close(source);
+ if ((sink != STDOUT_FILENO) && (sink != STDERR_FILENO))
+ ::close(sink);
}
class async_pipe;
diff --git a/boost/process/detail/posix/terminate.hpp b/boost/process/detail/posix/terminate.hpp
index 84024a5b34..e1e5f33fec 100644
--- a/boost/process/detail/posix/terminate.hpp
+++ b/boost/process/detail/posix/terminate.hpp
@@ -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)
diff --git a/boost/process/detail/posix/wait_for_exit.hpp b/boost/process/detail/posix/wait_for_exit.hpp
index 5eadc9725c..376e480251 100644
--- a/boost/process/detail/posix/wait_for_exit.hpp
+++ b/boost/process/detail/posix/wait_for_exit.hpp
@@ -54,11 +54,35 @@ inline bool wait_until(
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
-
::sigset_t sigset;
- sigemptyset(&sigset);
- sigaddset(&sigset, SIGCHLD);
+ //I need to set the signal, because it might be ignore / default, in which case sigwait might not work.
+
+ using _signal_t = void(*)(int);
+ static thread_local _signal_t sigchld_handler = SIG_DFL;
+
+ struct signal_interceptor_t
+ {
+ static void handler_func(int val)
+ {
+ if ((sigchld_handler != SIG_DFL) && (sigchld_handler != SIG_IGN))
+ sigchld_handler(val);
+ }
+ signal_interceptor_t() { sigchld_handler = ::signal(SIGCHLD, &handler_func); }
+ ~signal_interceptor_t() { ::signal(SIGCHLD, sigchld_handler); sigchld_handler = SIG_DFL;}
+
+ } signal_interceptor{};
+
+ if (sigemptyset(&sigset) != 0)
+ {
+ ec = get_last_error();
+ return false;
+ }
+ if (sigaddset(&sigset, SIGCHLD) != 0)
+ {
+ ec = get_last_error();
+ return false;
+ }
auto get_timespec =
[](const Duration & dur)
@@ -69,8 +93,8 @@ inline bool wait_until(
return ts;
};
- pid_t ret;
- int status;
+ int ret;
+ int status{0};
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
@@ -80,6 +104,7 @@ inline bool wait_until(
}
bool timed_out;
+
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
do
{
@@ -113,9 +138,16 @@ inline bool wait_until(
{
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
- ::nanosleep(&ts, &rem);
- while (rem.tv_sec > 0 || rem.tv_nsec > 0)
- ::nanosleep(&rem, &rem);
+ while (ts.tv_sec > 0 || ts.tv_nsec > 0)
+ {
+ if (::nanosleep(&ts, &rem) != 0)
+ {
+ auto err = errno;
+ if ((err == EINVAL) || (err == EFAULT))
+ break;
+ }
+ ts = get_timespec(time_out - Clock::now());
+ }
::exit(0);
}
@@ -125,7 +157,7 @@ inline bool wait_until(
~child_cleaner_t()
{
int res;
- ::kill(pid, -15);
+ ::kill(pid, SIGKILL);
::waitpid(pid, &res, WNOHANG);
}
};
@@ -133,19 +165,21 @@ inline bool wait_until(
do
{
- int ret_sig = 0;
+ int sig_{0};
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
- && (WIFEXITED(status) || WIFSIGNALED(status)))
- ret_sig = ::sigwait(&sigset, nullptr);
- errno = 0;
+ && (WIFEXITED(status) || WIFSIGNALED(status)))
- ret = ::waitpid(p.pid, &status, WNOHANG);
+ return false;
- if ((ret_sig == SIGCHLD) &&
+ ret = ::sigwait(&sigset, &sig_);
+ errno = 0;
+
+ 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)
diff --git a/boost/process/detail/posix/wait_group.hpp b/boost/process/detail/posix/wait_group.hpp
index 9dc2498031..d12cce1064 100644
--- a/boost/process/detail/posix/wait_group.hpp
+++ b/boost/process/detail/posix/wait_group.hpp
@@ -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,80 +105,58 @@ 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) && (errno != ESRCH))) && !(timed_out = (Clock::now() > time_out)));
- }
- 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)
+ if (errno != ECHILD)
{
ec = boost::process::detail::get_last_error();
- return true;
+ return !timed_out;
}
- else if (timeout_pid == 0)
+ else
{
- auto ts = get_timespec(time_out - Clock::now());
- ::setpgid(0, p.grp);
- ::nanosleep(&ts, nullptr);
- ::exit(0);
+ ec.clear();
+ return true; //even if timed out, there are no child proccessess left
}
- 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};
+#else
+ ::timespec sleep_interval;
+ sleep_interval.tv_sec = 0;
+ sleep_interval.tv_nsec = 1000000;
- do
- {
- int ret_sig = 0;
- int status;
- if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
- && (WIFEXITED(status) || WIFSIGNALED(status)))
- ret = ::sigwait(&sigset, nullptr);
- errno = 0;
- if ((ret == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
- old_sig.sa_handler(ret);
- ret = ::waitpid(-p.grp, &siginfo.si_status, 0); //so in case it exited, we wanna reap it first
+ while (!(timed_out = (Clock::now() > time_out)))
+ {
+ ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED | WNOHANG);
if (ret == -1)
{
- ec = get_last_error();
+ if ((errno == ECHILD) || (errno == ESRCH))
+ {
+ ec.clear();
+ return true;
+ }
+ ec = boost::process::detail::get_last_error();
return false;
}
-
- //check if we're done
- ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WNOHANG);
-
+ //we can wait, because unlike in the wait_for_exit, we have no race condition regarding eh exit code.
+ ::nanosleep(&sleep_interval, nullptr);
}
- while (((ret != -1) || (errno != ECHILD)) && !(timed_out = (Clock::now() > time_out)));
-
+ return !timed_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 >
diff --git a/boost/process/detail/used_handles.hpp b/boost/process/detail/used_handles.hpp
new file mode 100644
index 0000000000..4d56af3571
--- /dev/null
+++ b/boost/process/detail/used_handles.hpp
@@ -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_ */
diff --git a/boost/process/detail/windows/async_in.hpp b/boost/process/detail/windows/async_in.hpp
index 2a758337b8..cd45835af4 100644
--- a/boost/process/detail/windows/async_in.hpp
+++ b/boost/process/detail/windows/async_in.hpp
@@ -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)
{
}
diff --git a/boost/process/detail/windows/async_out.hpp b/boost/process/detail/windows/async_out.hpp
index 525cbafef0..ed39b03403 100644
--- a/boost/process/detail/windows/async_out.hpp
+++ b/boost/process/detail/windows/async_out.hpp
@@ -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)
{
diff --git a/boost/process/detail/windows/async_pipe.hpp b/boost/process/detail/windows/async_pipe.hpp
index f06653962a..06d5f2d854 100644
--- a/boost/process/detail/windows/async_pipe.hpp
+++ b/boost/process/detail/windows/async_pipe.hpp
@@ -40,23 +40,30 @@ 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))
{
- rhs._source.assign (::boost::winapi::INVALID_HANDLE_VALUE_);
- rhs._sink .assign (::boost::winapi::INVALID_HANDLE_VALUE_);
}
template<class CharT, class Traits = std::char_traits<CharT>>
explicit async_pipe(::boost::asio::io_context & ios_source,
@@ -191,13 +198,15 @@ public:
handle_type source(::boost::asio::io_context& ios) &&
{
::boost::asio::windows::stream_handle stolen(ios.get_executor(), _source.native_handle());
- _source.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
+ boost::system::error_code ec;
+ _source.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
return stolen;
}
handle_type sink (::boost::asio::io_context& ios) &&
{
::boost::asio::windows::stream_handle stolen(ios.get_executor(), _sink.native_handle());
- _sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
+ boost::system::error_code ec;
+ _sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
return stolen;
}
@@ -235,12 +244,11 @@ public:
}
};
-
-
async_pipe::async_pipe(const async_pipe& p) :
_source(const_cast<handle_type&>(p._source).get_executor()),
_sink (const_cast<handle_type&>(p._sink).get_executor())
{
+
auto proc = ::boost::winapi::GetCurrentProcess();
::boost::winapi::HANDLE_ source;
@@ -266,14 +274,16 @@ async_pipe::async_pipe(const async_pipe& p) :
::boost::winapi::DUPLICATE_SAME_ACCESS_))
throw_last_error("Duplicate Pipe Failed");
- _source.assign(source);
- _sink. assign(sink);
+ if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
+ _source.assign(source);
+ if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
+ _sink. assign(sink);
}
async_pipe::async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & ios_sink,
- const std::string & name) : _source(ios_source), _sink(ios_sink)
+ const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink)
{
static constexpr int FILE_FLAG_OVERLAPPED_ = 0x40000000; //temporary
@@ -285,7 +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_)
@@ -310,6 +320,44 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
_sink.assign(sink);
}
+template<class CharT, class Traits>
+async_pipe& async_pipe::operator=(const basic_pipe<CharT, Traits> & p)
+{
+ auto proc = ::boost::winapi::GetCurrentProcess();
+
+ ::boost::winapi::HANDLE_ source;
+ ::boost::winapi::HANDLE_ sink;
+
+ //cannot get the handle from a const object.
+ auto source_in = p.native_source();
+ auto sink_in = p.native_sink();
+
+ if (source_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
+ source = ::boost::winapi::INVALID_HANDLE_VALUE_;
+ else if (!::boost::winapi::DuplicateHandle(
+ proc, source_in.native_handle(), proc, &source, 0,
+ static_cast<::boost::winapi::BOOL_>(true),
+ ::boost::winapi::DUPLICATE_SAME_ACCESS_))
+ throw_last_error("Duplicate Pipe Failed");
+
+ if (sink_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
+ sink = ::boost::winapi::INVALID_HANDLE_VALUE_;
+ else if (!::boost::winapi::DuplicateHandle(
+ proc, sink_in.native_handle(), proc, &sink, 0,
+ static_cast<::boost::winapi::BOOL_>(true),
+ ::boost::winapi::DUPLICATE_SAME_ACCESS_))
+ throw_last_error("Duplicate Pipe Failed");
+
+ //so we also assign the io_context
+ if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
+ _source.assign(source);
+
+ if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
+ _sink.assign(sink);
+
+ return *this;
+}
+
async_pipe& async_pipe::operator=(const async_pipe & p)
{
auto proc = ::boost::winapi::GetCurrentProcess();
@@ -321,6 +369,8 @@ async_pipe& async_pipe::operator=(const async_pipe & p)
auto &source_in = const_cast<::boost::asio::windows::stream_handle &>(p._source);
auto &sink_in = const_cast<::boost::asio::windows::stream_handle &>(p._sink);
+ source_in.get_executor();
+
if (source_in.native_handle() == ::boost::winapi::INVALID_HANDLE_VALUE_)
source = ::boost::winapi::INVALID_HANDLE_VALUE_;
else if (!::boost::winapi::DuplicateHandle(
@@ -338,24 +388,23 @@ async_pipe& async_pipe::operator=(const async_pipe & p)
throw_last_error("Duplicate Pipe Failed");
//so we also assign the io_context
- _source = ::boost::asio::windows::stream_handle(source_in.get_executor(), source);
- _sink = ::boost::asio::windows::stream_handle(source_in.get_executor(), sink);
+ if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
+ _source = ::boost::asio::windows::stream_handle(source_in.get_executor(), source);
+ else
+ _source = ::boost::asio::windows::stream_handle(source_in.get_executor());
+
+ if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
+ _sink = ::boost::asio::windows::stream_handle(source_in.get_executor(), sink);
+ else
+ _sink = ::boost::asio::windows::stream_handle(source_in.get_executor());
return *this;
}
async_pipe& async_pipe::operator=(async_pipe && rhs)
{
- if (_source.native_handle() != ::boost::winapi::INVALID_HANDLE_VALUE_)
- ::boost::winapi::CloseHandle(_source.native_handle());
-
- if (_sink.native_handle() != ::boost::winapi::INVALID_HANDLE_VALUE_)
- ::boost::winapi::CloseHandle(_sink.native_handle());
-
- _source.assign(rhs._source.native_handle());
- _sink .assign(rhs._sink .native_handle());
- rhs._source.assign(::boost::winapi::INVALID_HANDLE_VALUE_);
- rhs._sink .assign(::boost::winapi::INVALID_HANDLE_VALUE_);
+ _source = std::move(rhs._source);
+ _sink = std::move(rhs._sink);
return *this;
}
diff --git a/boost/process/detail/windows/basic_pipe.hpp b/boost/process/detail/windows/basic_pipe.hpp
index 28f5651d75..3cf9044747 100644
--- a/boost/process/detail/windows/basic_pipe.hpp
+++ b/boost/process/detail/windows/basic_pipe.hpp
@@ -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");
diff --git a/boost/process/detail/windows/file_in.hpp b/boost/process/detail/windows/file_in.hpp
index c39235fb3d..c56f91ef08 100644
--- a/boost/process/detail/windows/file_in.hpp
+++ b/boost/process/detail/windows/file_in.hpp
@@ -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)))) {}
diff --git a/boost/process/detail/windows/file_out.hpp b/boost/process/detail/windows/file_out.hpp
index db14b3907d..88f0848d81 100644
--- a/boost/process/detail/windows/file_out.hpp
+++ b/boost/process/detail/windows/file_out.hpp
@@ -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)))) {}
diff --git a/boost/process/detail/windows/group_ref.hpp b/boost/process/detail/windows/group_ref.hpp
index bbbe4317ba..6e8fe71f2a 100644
--- a/boost/process/detail/windows/group_ref.hpp
+++ b/boost/process/detail/windows/group_ref.hpp
@@ -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())
{}
diff --git a/boost/process/detail/windows/handle_workaround.hpp b/boost/process/detail/windows/handle_workaround.hpp
new file mode 100644
index 0000000000..a6414ae3f6
--- /dev/null
+++ b/boost/process/detail/windows/handle_workaround.hpp
@@ -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_ */
diff --git a/boost/process/detail/windows/handles.hpp b/boost/process/detail/windows/handles.hpp
new file mode 100644
index 0000000000..5ba5780e9f
--- /dev/null
+++ b/boost/process/detail/windows/handles.hpp
@@ -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
diff --git a/boost/process/detail/windows/null_in.hpp b/boost/process/detail/windows/null_in.hpp
index 5fd9ffb9e2..d29eb8f415 100644
--- a/boost/process/detail/windows/null_in.hpp
+++ b/boost/process/detail/windows/null_in.hpp
@@ -14,14 +14,18 @@
#include <boost/winapi/handles.hpp>
#include <boost/winapi/handle_info.hpp>
#include <boost/process/detail/handler_base.hpp>
+#include <boost/process/detail/used_handles.hpp>
#include <boost/process/detail/windows/file_descriptor.hpp>
namespace boost { namespace process { namespace detail { namespace windows {
-struct null_in : public ::boost::process::detail::handler_base
+struct null_in : public ::boost::process::detail::handler_base, ::boost::process::detail::uses_handles
{
file_descriptor source{"NUL", file_descriptor::read};
+ ::boost::winapi::HANDLE_ get_used_handles() const { return source.handle(); }
+
+
public:
template <class WindowsExecutor>
void on_setup(WindowsExecutor &e) const
diff --git a/boost/process/detail/windows/null_out.hpp b/boost/process/detail/windows/null_out.hpp
index 0323deab78..0f80cf6cba 100644
--- a/boost/process/detail/windows/null_out.hpp
+++ b/boost/process/detail/windows/null_out.hpp
@@ -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;
};
diff --git a/boost/process/detail/windows/pipe_in.hpp b/boost/process/detail/windows/pipe_in.hpp
index 3cc1a10b90..39546eae62 100644
--- a/boost/process/detail/windows/pipe_in.hpp
+++ b/boost/process/detail/windows/pipe_in.hpp
@@ -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
diff --git a/boost/process/detail/windows/pipe_out.hpp b/boost/process/detail/windows/pipe_out.hpp
index 4cefa2fc28..125a34862d 100644
--- a/boost/process/detail/windows/pipe_out.hpp
+++ b/boost/process/detail/windows/pipe_out.hpp
@@ -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())
diff --git a/boost/process/detail/windows/wait_group.hpp b/boost/process/detail/windows/wait_group.hpp
index 33e353f998..d00fd0c561 100644
--- a/boost/process/detail/windows/wait_group.hpp
+++ b/boost/process/detail/windows/wait_group.hpp
@@ -27,7 +27,7 @@ inline bool wait_impl(const group_handle & p, std::error_code & ec, std::chrono:
while (workaround::get_queued_completion_status(
p._io_port, &completion_code,
- &completion_key, &overlapped, wait_time))
+ &completion_key, &overlapped, static_cast<::boost::winapi::DWORD_>(wait_time)))
{
if (reinterpret_cast<::boost::winapi::HANDLE_>(completion_key) == p._job_object &&
completion_code == workaround::JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO_)
@@ -48,11 +48,11 @@ inline bool wait_impl(const group_handle & p, std::error_code & ec, std::chrono:
return false; //correct, nothing left.
}
//reduce the remaining wait time -> in case interrupted by something else
- if (wait_time != ::boost::winapi::infinite)
+ if (wait_time != static_cast<int>(::boost::winapi::infinite))
{
auto now = std::chrono::system_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
- wait_time -= diff.count();
+ wait_time -= static_cast<std::chrono::system_clock::rep>(diff.count());
start_time = now;
if (wait_time <= 0)
return true; //timeout with other source
diff --git a/boost/process/extend.hpp b/boost/process/extend.hpp
index 5d4eb2d515..3b022c69fe 100644
--- a/boost/process/extend.hpp
+++ b/boost/process/extend.hpp
@@ -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;
diff --git a/boost/process/handles.hpp b/boost/process/handles.hpp
new file mode 100644
index 0000000000..23598f3108
--- /dev/null
+++ b/boost/process/handles.hpp
@@ -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_
diff --git a/boost/process/pipe.hpp b/boost/process/pipe.hpp
index dd4af09d63..2040c78e3d 100644
--- a/boost/process/pipe.hpp
+++ b/boost/process/pipe.hpp
@@ -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;