diff options
Diffstat (limited to 'boost/process')
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; |