diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c392d825..269233dd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,19 +23,19 @@ jobs: include: - { toolset: gcc-5, cxxstd: "11,14,1z", os: ubuntu-latest, container: 'ubuntu:18.04', install: g++-5 } - { toolset: gcc-6, cxxstd: "11,14,1z", os: ubuntu-latest, container: 'ubuntu:18.04', install: g++-6 } - - { toolset: gcc-7, cxxstd: "11,14,17", os: ubuntu-20.04, install: g++-7 } + - { toolset: gcc-7, cxxstd: "11,14,17", os: ubuntu-latest, container: 'ubuntu:20.04', install: g++-7 } - { toolset: gcc-10, cxxstd: "11,14,17,2a", os: ubuntu-22.04, install: g++-10 } - { toolset: gcc-12, cxxstd: "11,14,17,20,2b", os: ubuntu-22.04, install: g++-12 } - { toolset: clang, compiler: clang++-3.9, cxxstd: "11,14", os: ubuntu-latest, container: 'ubuntu:18.04', install: clang-3.9 } - { toolset: clang, compiler: clang++-4.0, cxxstd: "11,14", os: ubuntu-latest, container: 'ubuntu:18.04', install: clang-4.0 } - { toolset: clang, compiler: clang++-5.0, cxxstd: "11,14,1z", os: ubuntu-latest, container: 'ubuntu:18.04', install: clang-5.0 } - - { toolset: clang, compiler: clang++-6.0, cxxstd: "11,14,17", os: ubuntu-20.04, install: clang-6.0 } - - { toolset: clang, compiler: clang++-7, cxxstd: "11,14,17", os: ubuntu-20.04, install: clang-7 } - - { toolset: clang, compiler: clang++-8, cxxstd: "11,14,17", os: ubuntu-20.04, install: clang-8 } - - { toolset: clang, compiler: clang++-9, cxxstd: "11,14,17,2a", os: ubuntu-20.04, install: clang-9 } - - { toolset: clang, compiler: clang++-10, cxxstd: "11,14,17,2a", os: ubuntu-20.04, install: clang-10 } - - { toolset: clang, compiler: clang++-11, cxxstd: "11,14,17,2a", os: ubuntu-20.04, install: clang-11 } - - { toolset: clang, compiler: clang++-12, cxxstd: "11,14,17,2a", os: ubuntu-20.04, install: clang-12 } + - { toolset: clang, compiler: clang++-6.0, cxxstd: "11,14,17", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-6.0 } + - { toolset: clang, compiler: clang++-7, cxxstd: "11,14,17", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-7 } + - { toolset: clang, compiler: clang++-8, cxxstd: "11,14,17", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-8 } + - { toolset: clang, compiler: clang++-9, cxxstd: "11,14,17,2a", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-9 } + - { toolset: clang, compiler: clang++-10, cxxstd: "11,14,17,2a", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-10 } + - { toolset: clang, compiler: clang++-11, cxxstd: "11,14,17,2a", os: ubuntu-22.04, install: clang-11 } + - { toolset: clang, compiler: clang++-12, cxxstd: "11,14,17,2a", os: ubuntu-22.04, install: clang-12 } - { toolset: clang, compiler: clang++-13, cxxstd: "11,14,17,20,2b", os: ubuntu-22.04, install: clang-13 } - { toolset: clang, compiler: clang++-14, cxxstd: "11,14,17,20,2b", os: ubuntu-22.04, install: clang-14 } - { toolset: clang, cxxstd: "11,14,17,2a", os: macos-13 } diff --git a/CMakeLists.txt b/CMakeLists.txt index 19826d337..348670195 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ cmake_minimum_required(VERSION 3.5...3.16) project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) +option(BOOST_PROCESS_USE_STD_FS "Use std::filesystem instead of Boost.Filesystem" OFF) + add_library(boost_process src/detail/environment_posix.cpp src/detail/environment_win.cpp @@ -49,13 +51,14 @@ target_compile_definitions(boost_process PRIVATE BOOST_PROCESS_SOURCE=1 ) -if (BOOST_PROCESS_USE_STD_FS) - target_compile_definitions(boost_process PUBLIC BOOST_PROCESS_USE_STD_FS=1 ) +if(BOOST_PROCESS_USE_STD_FS) + target_compile_definitions(boost_process PUBLIC BOOST_PROCESS_USE_STD_FS) + target_compile_features(boost_process PUBLIC cxx_std_17) else() target_link_libraries(boost_process PUBLIC Boost::filesystem) endif() -if (WIN32) +if(WIN32) target_link_libraries(boost_process PUBLIC ntdll shell32 advapi32 user32 ws2_32) endif() diff --git a/doc/acknowledgements.adoc b/doc/acknowledgements.adoc index 3b7c6d387..3fb0d92c7 100644 --- a/doc/acknowledgements.adoc +++ b/doc/acknowledgements.adoc @@ -8,6 +8,6 @@ A special thank you goes to [http://www.intra2net.com/(Intra2net AG) (especially Great thanks also goes to Boris Schaeling, who despite having boost.process rejected, went on to work on it and maintained it up until this day and participated in the development of the current version. -Many Thanks, to [https://github.com/time-killer-games](Samuel Venable) for contributing the <> functionality and all the research that went into it. +Many Thanks, to [https://github.com/samuelvenable](Samuel Venable) for contributing the <> functionality and all the research that went into it. diff --git a/doc/reference/environment.adoc b/doc/reference/environment.adoc index b861285da..dac0bdf46 100644 --- a/doc/reference/environment.adoc +++ b/doc/reference/environment.adoc @@ -3,7 +3,7 @@ === `environment` -The `environment` header provides facilities to maniuplate the current environment and set it for new processes. +The `environment` header provides facilities to manipulate the current environment and set it for new processes. An environment is a a `range` of `T` fulfilling these requirements: @@ -22,7 +22,7 @@ namespace environment // A char traits type that reflects the OS rules for string representing environment keys. /* Can be an alias of std::char_traits. May only be defined for `char` and `wchar_t`. * - * Windows treats keys as case-insensitive yet perserving. The char traits are made to reflect + * Windows treats keys as case-insensitive yet preserving. The char traits are made to reflect * that behaviour. */ template diff --git a/doc/version2.adoc b/doc/version2.adoc index 1af14c8c5..d5ada523f 100644 --- a/doc/version2.adoc +++ b/doc/version2.adoc @@ -13,7 +13,7 @@ The major changes are * separate compilation * fd safe by default -Version 2 is now the defauled. In order to discourage usage of the deprecated v1, it's documentation has been removed. +Version 2 is now the default. In order to discourage usage of the deprecated v1, it's documentation has been removed. == Simplified Interface diff --git a/include/boost/process/v1/async.hpp b/include/boost/process/v1/async.hpp index d701d921f..d70970217 100644 --- a/include/boost/process/v1/async.hpp +++ b/include/boost/process/v1/async.hpp @@ -114,7 +114,7 @@ io_context ios; child c("ls", ios, on_exit=[](int exit, const std::error_code& ec_in){}); std::future exit_code; -chlid c2("ls", ios, on_exit=exit_code); +child c2("ls", ios, on_exit=exit_code); \endcode diff --git a/include/boost/process/v1/async_pipe.hpp b/include/boost/process/v1/async_pipe.hpp index 642c18943..f14ee9fd3 100644 --- a/include/boost/process/v1/async_pipe.hpp +++ b/include/boost/process/v1/async_pipe.hpp @@ -26,7 +26,7 @@ namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { #if defined(BOOST_PROCESS_DOXYGEN) -/** Class implementing an asnychronous I/O-Object for use with boost.asio. +/** Class implementing an asynchronous I/O-Object for use with boost.asio. * It is based on the corresponding I/O Object, that is either boost::asio::windows::stream_handle or * boost::asio::posix::stream_descriptor. * diff --git a/include/boost/process/v1/child.hpp b/include/boost/process/v1/child.hpp index 5967f9ed4..41b2410c2 100644 --- a/include/boost/process/v1/child.hpp +++ b/include/boost/process/v1/child.hpp @@ -128,7 +128,7 @@ class child /** Same as valid, for convenience. */ explicit operator bool() const; - /** Check if the the chlid process is in any process group. */ + /** Check if the the child process is in any process group. */ bool in_group() const; /** \overload bool in_group() const */ diff --git a/include/boost/process/v1/detail/windows/async_pipe.hpp b/include/boost/process/v1/detail/windows/async_pipe.hpp index 718f300d4..045a8c17c 100644 --- a/include/boost/process/v1/detail/windows/async_pipe.hpp +++ b/include/boost/process/v1/detail/windows/async_pipe.hpp @@ -31,6 +31,8 @@ inline std::string make_pipe_name() static std::atomic_size_t cnt{0}; name += std::to_string(pid); name += "_"; + name += std::to_string(intptr_t(&cnt)); // to unclash Boost instances in plug-in DLLs + name += "_"; name += std::to_string(cnt++); return name; @@ -60,8 +62,6 @@ class async_pipe async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink, const std::string & name) : async_pipe(ios_source, ios_sink, name, false) {} - - inline async_pipe(const async_pipe& rhs); async_pipe(async_pipe&& rhs) : _source(std::move(rhs._source)), _sink(std::move(rhs._sink)) { @@ -153,7 +153,6 @@ class async_pipe return _sink.write_some(buffers); } - template std::size_t read_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept { @@ -281,7 +280,6 @@ async_pipe::async_pipe(const async_pipe& p) : _sink. assign(sink); } - async_pipe::async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink, const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink) @@ -298,7 +296,6 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source, | FILE_FLAG_OVERLAPPED_, //write flag 0, private_ ? 1 : ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr); - if (source == boost::winapi::INVALID_HANDLE_VALUE_) ::boost::process::v1::detail::throw_last_error("create_named_pipe(" + name + ") failed"); diff --git a/include/boost/process/v1/locale.hpp b/include/boost/process/v1/locale.hpp index d7d07575d..e6b104812 100644 --- a/include/boost/process/v1/locale.hpp +++ b/include/boost/process/v1/locale.hpp @@ -78,8 +78,9 @@ inline std::locale default_locale() std::locale global_loc = std::locale(); return std::locale(global_loc, new std::codecvt_utf8); # else // Other POSIX - // Return a default locale object. - return std::locale(); + // ISO C calls std::locale("") "the locale-specific native environment", and this + // locale is the default for many POSIX-based operating systems such as Linux. + return std::locale(""); # endif } diff --git a/include/boost/process/v2/default_launcher.hpp b/include/boost/process/v2/default_launcher.hpp index a95570758..452b35940 100644 --- a/include/boost/process/v2/default_launcher.hpp +++ b/include/boost/process/v2/default_launcher.hpp @@ -18,6 +18,8 @@ #else #if defined(BOOST_PROCESS_V2_PDFORK) #include +#elif defined(BOOST_PROCESS_V2_PIPEFORK) +#include #else #include #endif @@ -48,6 +50,8 @@ typedef windows::default_launcher default_process_launcher; #else #if defined(BOOST_PROCESS_V2_PDFORK) typedef posix::pdfork_launcher default_process_launcher; +#elif defined(BOOST_PROCESS_V2_PIPEFORK) +typedef posix::pipe_fork_launcher default_process_launcher; #else typedef posix::default_launcher default_process_launcher; #endif diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp index 24f374591..43d5b011b 100644 --- a/include/boost/process/v2/detail/config.hpp +++ b/include/boost/process/v2/detail/config.hpp @@ -165,7 +165,7 @@ BOOST_PROCESS_V2_END_NAMESPACE #include -#if defined(SYS_pidfd_open) +#if defined(SYS_pidfd_open) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN) #define BOOST_PROCESS_V2_PIDFD_OPEN 1 #define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1 #endif diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index 9a0363355..8af291d96 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -255,7 +255,7 @@ struct basic_process_handle_fd ec.clear(); exit_code = code; } - return false; + return false; } bool running(native_exit_code_type &exit_code) diff --git a/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp b/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp index 5c2416765..73c0e2be0 100644 --- a/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp @@ -18,19 +18,27 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) #include +#include +#include #include #include #include #include -#include +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) +#include +#endif #else #include +#include +#include #include #include #include #include +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) #include #endif +#endif BOOST_PROCESS_V2_BEGIN_NAMESPACE @@ -45,7 +53,7 @@ struct basic_process_handle_fd_or_signal typedef Executor executor_type; executor_type get_executor() - { return signal_set_.get_executor(); } + { return descriptor_.get_executor(); } /// Rebinds the process_handle to another executor. template @@ -277,14 +285,13 @@ struct basic_process_handle_fd_or_signal int res = ::waitpid(pid_, &code, WNOHANG); if (res == -1) ec = get_last_error(); - else - ec.clear(); - - if (process_is_running(res)) + else if (res == 0) return true; else + { + ec.clear(); exit_code = code; - + } return false; } @@ -311,12 +318,19 @@ struct basic_process_handle_fd_or_signal struct basic_process_handle_fd_or_signal; pid_type pid_ = -1; net::posix::basic_stream_descriptor descriptor_; +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) net::basic_signal_set signal_set_{descriptor_.get_executor(), SIGCHLD}; - +#else + int signal_set_; +#endif struct async_wait_op_ { net::posix::basic_descriptor &descriptor; +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) net::basic_signal_set &handle; +#else + int dummy; +#endif pid_type pid_; bool needs_post = true; @@ -343,35 +357,41 @@ struct basic_process_handle_fd_or_signal if (!ec && (wait_res == 0)) { - needs_post = false; if (descriptor.is_open()) - descriptor.async_wait( - net::posix::descriptor_base::wait_read, - std::move(self)); + { + needs_post = false; + descriptor.async_wait( + net::posix::descriptor_base::wait_read, + std::move(self)); + return; + } else - handle.async_wait(std::move(self)); - return; - } - - struct completer - { - error_code ec; - native_exit_code_type code; - typename std::decay::type self; - - void operator()() { - self.complete(ec, code); +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) + needs_post = false; + handle.async_wait(std::move(self)); + return; +#else + BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported); +#endif } - }; + } - const auto exec = self.get_executor(); - completer cpl{ec, exit_code, std::move(self)}; if (needs_post) - net::post(exec, std::move(cpl)); + { + auto exec = net::get_associated_immediate_executor(self, descriptor.get_executor()); + net::dispatch(exec, net::append(std::move(self), exit_code, ec)); + } else - net::dispatch(exec, std::move(cpl)); - + { + auto exec = net::get_associated_executor(self); + net::dispatch(exec, net::append(std::move(self), exit_code, ec)); + } + } + template + void operator()(Self &&self, native_exit_code_type code, error_code ec) + { + self.complete(ec, code); } }; public: diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index 6cf5ec893..098ebb7ba 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -17,17 +17,25 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) #include +#include +#include #include #include #include +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) #include +#endif #else #include +#include +#include #include #include #include +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) #include #endif +#endif BOOST_PROCESS_V2_BEGIN_NAMESPACE @@ -86,9 +94,14 @@ struct basic_process_handle_signal basic_process_handle_signal& operator=(basic_process_handle_signal && handle) { pid_ = handle.id(); +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) + signal_set_.~basic_signal_set(); using ss = net::basic_signal_set; new (&signal_set_) ss(handle.get_executor(), SIGCHLD); +#else + signal_set_.executor = handle.signal_set_.executor; +#endif handle.pid_ = -1; return *this; } @@ -244,11 +257,13 @@ struct basic_process_handle_signal int res = ::waitpid(pid_, &code, WNOHANG); if (res == -1) ec = get_last_error(); - - if (res == 0) + else if (res == 0) return true; else + { + ec.clear(); exit_code = code; + } return false; } @@ -273,10 +288,24 @@ struct basic_process_handle_signal template friend struct basic_process_handle_signal; pid_type pid_ = -1; +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) net::basic_signal_set signal_set_; - +#else + struct signal_set_dummy_ + { + signal_set_dummy_(signal_set_dummy_ &&) = default; + signal_set_dummy_(const signal_set_dummy_ &) = default; + Executor executor; + using executor_type = Executor; + executor_type get_executor() {return executor;} + signal_set_dummy_(Executor executor, int) : executor(std::move(executor)) {} + }; + signal_set_dummy_ signal_set_; +#endif struct async_wait_op_ { +#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET) + net::basic_signal_set &handle; pid_type pid_; @@ -290,7 +319,7 @@ struct basic_process_handle_signal } template - void operator()(Self &&self, error_code ec, int sig) + void operator()(Self &&self, error_code ec, int /*sig*/) { if (ec == net::error::operation_aborted && self.get_cancellation_state().cancelled() @@ -315,20 +344,25 @@ struct basic_process_handle_signal return; } - struct completer - { - error_code ec; - native_exit_code_type code; - typename std::decay::type self; - - void operator()() - { - self.complete(ec, code); - } - }; - const auto exec = self.get_executor(); - net::dispatch(exec, completer{ec, exit_code, std::move(self)}); + net::dispatch(exec, net::append(std::move(self), exit_code, ec)); + } +#else + signal_set_dummy_ dummy_; + pid_t pid; + template + void operator()(Self &&self) + { + auto exec = net::get_associated_immediate_executor(self, dummy_.get_executor()); + error_code ec; + BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported); + net::dispatch(exec, net::append(std::move(self), native_exit_code_type(), ec)); + } +#endif + template + void operator()(Self &&self, native_exit_code_type code, error_code ec) + { + self.complete(ec, code); } }; public: diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index 0c6a0875b..c38064c65 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -40,7 +40,7 @@ namespace environment /// A char traits type that reflects the OS rules for string representing environment keys. /** Can be an alias of std::char_traits. May only be defined for `char` and `wchar_t`. * - * Windows treats keys as case-insensitive yet perserving. The char traits are made to reflect + * Windows treats keys as case-insensitive yet preserving. The char traits are made to reflect * that behaviour. */ template @@ -1372,6 +1372,8 @@ struct current_view environment::native_iterator iterator_; }; + using const_iterator = iterator; + iterator begin() const {return iterator(handle_.get());} iterator end() const {return iterator(detail::find_end(handle_.get()));} diff --git a/include/boost/process/v2/posix/pipe_fork_launcher.hpp b/include/boost/process/v2/posix/pipe_fork_launcher.hpp new file mode 100644 index 000000000..415c529f0 --- /dev/null +++ b/include/boost/process/v2/posix/pipe_fork_launcher.hpp @@ -0,0 +1,185 @@ +// Copyright (c) 2022 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_V2_POSIX_PIPE_FORK_LAUNCHER_HPP +#define BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP + +#include + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace posix +{ + +/// A launcher using `pipe_fork`. Default on FreeBSD +struct pipe_fork_launcher : default_launcher +{ + /// The file descriptor of the subprocess. Set after fork. + pipe_fork_launcher() = default; + + template + auto operator()(ExecutionContext & context, + const typename std::enable_if::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + error_code ec; + auto proc = (*this)(context, ec, executable, std::forward(args), std::forward(inits)...); + + if (ec) + v2::detail::throw_error(ec, "pipe_fork_launcher"); + + return proc; + } + + + template + auto operator()(ExecutionContext & context, + error_code & ec, + const typename std::enable_if::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + return (*this)(context.get_executor(), ec, executable, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + const typename std::enable_if< + net::execution::is_executor::value || + net::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + error_code ec; + auto proc = (*this)(std::move(exec), ec, executable, std::forward(args), std::forward(inits)...); + + if (ec) + v2::detail::throw_error(ec, "pipe_fork_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + net::execution::is_executor::value || + net::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto argv = this->build_argv_(executable, std::forward(args)); + int fd = -1; + { + pipe_guard pg, pg_wait; + if (::pipe(pg.p)) + { + BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); + return basic_process{exec}; + } + if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC)) + { + BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); + return basic_process{exec}; + } + if (::pipe(pg_wait.p)) + { + BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); + return basic_process{exec}; + } + if (::fcntl(pg_wait.p[1], F_SETFD, FD_CLOEXEC)) + { + BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); + return basic_process{exec}; + } + ec = detail::on_setup(*this, executable, argv, inits ...); + if (ec) + { + detail::on_error(*this, executable, argv, ec, inits...); + return basic_process(exec); + } + fd_whitelist.push_back(pg.p[1]); + fd_whitelist.push_back(pg_wait.p[1]); + + auto & ctx = net::query( + exec, net::execution::context); + ctx.notify_fork(net::execution_context::fork_prepare); + pid = ::fork(); + if (pid == -1) + { + ctx.notify_fork(net::execution_context::fork_parent); + detail::on_fork_error(*this, executable, argv, ec, inits...); + detail::on_error(*this, executable, argv, ec, inits...); + + BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); + return basic_process{exec}; + } + else if (pid == 0) + { + ctx.notify_fork(net::execution_context::fork_child); + ::close(pg.p[0]); + + ec = detail::on_exec_setup(*this, executable, argv, inits...); + if (!ec) + { + close_all_fds(ec); + } + if (!ec) + ::execve(executable.c_str(), const_cast(argv), const_cast(env)); + + default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int))); + BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); + detail::on_exec_error(*this, executable, argv, ec, inits...); + ::exit(EXIT_FAILURE); + return basic_process{exec}; + } + ctx.notify_fork(net::execution_context::fork_parent); + ::close(pg.p[1]); + pg.p[1] = -1; + ::close(pg_wait.p[1]); + pg_wait.p[1] = -1; + int child_error{0}; + int count = -1; + while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1) + { + int err = errno; + if ((err != EAGAIN) && (err != EINTR)) + { + BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category()); + break; + } + } + if (count != 0) + BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category()); + + if (ec) + { + detail::on_error(*this, executable, argv, ec, inits...); + return basic_process{exec}; + } + std::swap(fd, pg_wait.p[0]); + } + + basic_process proc(exec, pid, fd); + detail::on_success(*this, executable, argv, ec, inits...); + return proc; + } +}; + + +} + +BOOST_PROCESS_V2_END_NAMESPACE + + +#endif //BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index 9c96d23b7..5643e3927 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -231,7 +231,7 @@ struct basic_process void resume() { error_code ec; - suspend(ec); + resume(ec); if (ec) detail::throw_error(ec, "resume"); } diff --git a/include/boost/process/v2/process_handle.hpp b/include/boost/process/v2/process_handle.hpp index cc00a069c..86c86d2ad 100644 --- a/include/boost/process/v2/process_handle.hpp +++ b/include/boost/process/v2/process_handle.hpp @@ -13,7 +13,7 @@ #if defined(BOOST_PROCESS_V2_PIDFD_OPEN) #include -#elif defined(BOOST_PROCESS_V2_PDFORK) +#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPEFORK) #include #else // with asio support we could use EVFILT_PROC:NOTE_EXIT as well. @@ -107,9 +107,9 @@ struct basic_process_handle void request_exit() /// Unconditionally terminates the process and stores the exit code in exit_status. - void terminate(native_exit_code_type &exit_status, error_code &ec);\ + void terminate(native_exit_code_type &exit_status, error_code &ec); /// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec) - void terminate(native_exit_code_type &exit_status);/ + void terminate(native_exit_code_type &exit_status); /// Checks if the current process is running. /**If it has already completed, it assigns the exit code to `exit_code`. @@ -137,7 +137,7 @@ using basic_process_handle = detail::basic_process_handle_win; #if defined(BOOST_PROCESS_V2_PIDFD_OPEN) template using basic_process_handle = detail::basic_process_handle_fd; -#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPE_LAUNCHER) +#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPEFORK) template using basic_process_handle = detail::basic_process_handle_fd_or_signal; #else diff --git a/include/boost/process/v2/stdio.hpp b/include/boost/process/v2/stdio.hpp index f1bf08049..47030c7fe 100644 --- a/include/boost/process/v2/stdio.hpp +++ b/include/boost/process/v2/stdio.hpp @@ -31,8 +31,33 @@ #endif BOOST_PROCESS_V2_BEGIN_NAMESPACE + + +template +struct is_readable_pipe : std::false_type +{ +}; + +template +struct is_readable_pipe> : std::true_type +{ +}; + + +template +struct is_writable_pipe : std::false_type +{ +}; + +template +struct is_writable_pipe> : std::true_type +{ +}; + namespace detail { + + #if defined(BOOST_PROCESS_V2_WINDOWS) struct handle_closer @@ -104,16 +129,10 @@ struct process_io_binding } - template - process_io_binding(net::basic_readable_pipe & pipe) + template + process_io_binding(ReadablePipe & pipe, + typename std::enable_if::value && Target != STD_INPUT_HANDLE>::type * = nullptr) { - if (Target == STD_INPUT_HANDLE) - { - auto h_ = pipe.native_handle(); - h = std::unique_ptr{h_, get_flags(h_)}; - return ; - } - net::detail::native_pipe_handle p[2]; error_code ec; net::detail::create_pipe(p, ec); @@ -125,15 +144,10 @@ struct process_io_binding } - template - process_io_binding(net::basic_writable_pipe & pipe) + template + process_io_binding(WritablePipe & pipe, + typename std::enable_if::value && Target == STD_INPUT_HANDLE>::type * = nullptr) { - if (Target != STD_INPUT_HANDLE) - { - auto h_ = pipe.native_handle(); - h = std::unique_ptr{h_, get_flags(h_)}; - return ; - } net::detail::native_pipe_handle p[2]; error_code ec; net::detail::create_pipe(p, ec); @@ -207,15 +221,10 @@ struct process_io_binding { } - template - process_io_binding(net::basic_readable_pipe & readable_pipe) + template + process_io_binding(ReadablePipe & readable_pipe, + typename std::enable_if::value && Target != STDIN_FILENO>::type * = nullptr) { - if (Target == STDIN_FILENO) - { - fd = readable_pipe.native_handle(); - return ; - } - net::detail::native_pipe_handle p[2]; net::detail::create_pipe(p, ec); if (ec) @@ -232,15 +241,10 @@ struct process_io_binding } - template - process_io_binding(net::basic_writable_pipe & writable_pipe) + template + process_io_binding(WritablePipe & writable_pipe, + typename std::enable_if::value && Target == STDIN_FILENO>::type * = nullptr) { - - if (Target != STDIN_FILENO) - { - fd = writable_pipe.native_handle(); - return ; - } net::detail::native_pipe_handle p[2]; error_code ec; net::detail::create_pipe(p, ec); @@ -263,7 +267,7 @@ struct process_io_binding return ec; } - error_code on_exec_setup(posix::default_launcher & launcher, + error_code on_exec_setup(posix::default_launcher &, const filesystem::path &, const char * const *) { if (::dup2(fd, target) == -1) diff --git a/include/boost/process/v2/windows/default_launcher.hpp b/include/boost/process/v2/windows/default_launcher.hpp index d0ca19b4d..29483b8c8 100644 --- a/include/boost/process/v2/windows/default_launcher.hpp +++ b/include/boost/process/v2/windows/default_launcher.hpp @@ -109,7 +109,7 @@ inline std::false_type probe_on_error( template inline auto probe_on_error(Launcher & launcher, Init && init, derived && ) - -> std::is_same(), std::declval(), std::declval()))>; + -> std::is_same(), std::declval(), std::declval()))>; template using has_on_error = decltype(probe_on_error(std::declval(), std::declval(), derived{})); diff --git a/index.html b/index.html index ab4fac27e..898896b4a 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,7 @@ - + Boost.Process