From 42bdfb554517c150b79d167753faf071b4827180 Mon Sep 17 00:00:00 2001 From: James Baker Date: Fri, 28 Aug 2020 11:13:39 -0400 Subject: [PATCH 001/397] Fixed file_descriptor move assignment operator to return a reference to 'this'. Issue # 219 --- include/boost/process/detail/windows/file_descriptor.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/process/detail/windows/file_descriptor.hpp b/include/boost/process/detail/windows/file_descriptor.hpp index e00c96d10..e192ad14f 100644 --- a/include/boost/process/detail/windows/file_descriptor.hpp +++ b/include/boost/process/detail/windows/file_descriptor.hpp @@ -102,6 +102,7 @@ struct file_descriptor if (_handle != ::boost::winapi::INVALID_HANDLE_VALUE_) ::boost::winapi::CloseHandle(_handle); _handle = boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_); + return &this; } ~file_descriptor() From baa8d3fe7c3f5d4a56353c164080ae0e253894f2 Mon Sep 17 00:00:00 2001 From: James Baker Date: Fri, 28 Aug 2020 12:43:16 -0400 Subject: [PATCH 002/397] Returning *this instead of erroneous *this. Issue # 219 --- include/boost/process/detail/windows/file_descriptor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/detail/windows/file_descriptor.hpp b/include/boost/process/detail/windows/file_descriptor.hpp index e192ad14f..187f6ebab 100644 --- a/include/boost/process/detail/windows/file_descriptor.hpp +++ b/include/boost/process/detail/windows/file_descriptor.hpp @@ -102,7 +102,7 @@ struct file_descriptor if (_handle != ::boost::winapi::INVALID_HANDLE_VALUE_) ::boost::winapi::CloseHandle(_handle); _handle = boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_); - return &this; + return *this; } ~file_descriptor() From b526ac7ce538d0795b56d451b8ba9b8ee66001f9 Mon Sep 17 00:00:00 2001 From: Shauren Date: Sat, 16 Jan 2021 17:02:41 +0100 Subject: [PATCH 003/397] Fix posix implementation of move constructor/assignment in file_descriptor --- .../process/detail/posix/file_descriptor.hpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/file_descriptor.hpp b/include/boost/process/detail/posix/file_descriptor.hpp index 0dcb99caf..4b481737b 100644 --- a/include/boost/process/detail/posix/file_descriptor.hpp +++ b/include/boost/process/detail/posix/file_descriptor.hpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace boost { namespace process { namespace detail { namespace posix { @@ -39,10 +40,22 @@ struct file_descriptor } file_descriptor(const file_descriptor & ) = delete; - file_descriptor(file_descriptor && ) = default; + file_descriptor(file_descriptor &&other) + : _handle(boost::exchange(other._handle, -1)) + { + } file_descriptor& operator=(const file_descriptor & ) = delete; - file_descriptor& operator=(file_descriptor && ) = default; + file_descriptor& operator=(file_descriptor &&other) + { + if (this != &other) + { + if (_handle != -1) + ::close(_handle); + _handle = boost::exchange(other._handle, -1); + } + return *this; + } ~file_descriptor() { From 8c5ab0219246527e4e19f8d3850b323881d31ae4 Mon Sep 17 00:00:00 2001 From: George Pimm Date: Wed, 14 Apr 2021 13:52:46 +0100 Subject: [PATCH 004/397] Add include to extend.hpp for memory. extend.hpp uses std::shared_ptr as a member but does not include it --- include/boost/process/extend.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/process/extend.hpp b/include/boost/process/extend.hpp index 3b022c69f..fbbadc6ad 100644 --- a/include/boost/process/extend.hpp +++ b/include/boost/process/extend.hpp @@ -8,6 +8,7 @@ #include #include +#include #if defined(BOOST_WINDOWS_API) #include From 3d092498b2bc1a155451246a2957ad8e4fee67fb Mon Sep 17 00:00:00 2001 From: Valentyn Pavliuchenko Date: Thu, 19 Aug 2021 19:54:42 +0300 Subject: [PATCH 005/397] Fix child construction from pid_t (it takes reference to pid_t for no reason). --- include/boost/process/detail/child_decl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/detail/child_decl.hpp b/include/boost/process/detail/child_decl.hpp index 36f246564..48f02172d 100644 --- a/include/boost/process/detail/child_decl.hpp +++ b/include/boost/process/detail/child_decl.hpp @@ -60,7 +60,7 @@ class child explicit child(child_handle &&ch, const std::shared_ptr> &ptr) : _child_handle(std::move(ch)), _exit_status(ptr) {} explicit child(child_handle &&ch) : _child_handle(std::move(ch)) {} - explicit child(pid_t & pid) : _child_handle(pid), _attached(false) {}; + explicit child(const pid_t & pid) : _child_handle(pid), _attached(false) {}; child(const child&) = delete; child(child && lhs) noexcept : _child_handle(std::move(lhs._child_handle)), From aefb990a7a89e5c7c54c011874348e7e786de2c4 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 3 Oct 2021 10:30:10 +0300 Subject: [PATCH 006/397] Remove triling whitespace --- include/boost/process/detail/posix/group_handle.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/detail/posix/group_handle.hpp b/include/boost/process/detail/posix/group_handle.hpp index 534eb08b2..8438b9099 100644 --- a/include/boost/process/detail/posix/group_handle.hpp +++ b/include/boost/process/detail/posix/group_handle.hpp @@ -42,7 +42,7 @@ struct group_handle } void add(handle_t proc) - { + { if (::setpgid(proc, grp)) throw_last_error(); } From a13a60d4285e6892a2b8bee144039a0453192aea Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:19:32 +0800 Subject: [PATCH 007/397] Removed unneeded WNOHANG. --- include/boost/process/detail/posix/terminate.hpp | 2 +- include/boost/process/detail/posix/wait_for_exit.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/terminate.hpp b/include/boost/process/detail/posix/terminate.hpp index e1e5f33fe..a707ea501 100644 --- a/include/boost/process/detail/posix/terminate.hpp +++ b/include/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, WNOHANG); //just to clean it up + ::waitpid(p.pid, &status, 0); //should not be WNOHANG, since that would allow zombies. } inline void terminate(const child_handle &p) diff --git a/include/boost/process/detail/posix/wait_for_exit.hpp b/include/boost/process/detail/posix/wait_for_exit.hpp index 376e48025..de8153b81 100644 --- a/include/boost/process/detail/posix/wait_for_exit.hpp +++ b/include/boost/process/detail/posix/wait_for_exit.hpp @@ -158,7 +158,7 @@ inline bool wait_until( { int res; ::kill(pid, SIGKILL); - ::waitpid(pid, &res, WNOHANG); + ::waitpid(pid, &res, 0); } }; child_cleaner_t child_cleaner{timeout_pid}; From 6d08cb369ea7d893d41c595db4f250a01c379591 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:28:07 +0800 Subject: [PATCH 008/397] Closes boostorg/process#190 --- include/boost/process/detail/posix/async_pipe.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/async_pipe.hpp b/include/boost/process/detail/posix/async_pipe.hpp index 1766ce2f9..ea3bbe0ce 100644 --- a/include/boost/process/detail/posix/async_pipe.hpp +++ b/include/boost/process/detail/posix/async_pipe.hpp @@ -25,6 +25,11 @@ class async_pipe typedef ::boost::asio::posix::stream_descriptor handle_type; typedef typename handle_type::executor_type executor_type; + executor_type get_executor() + { + return _source.get_executor(); + } + inline async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios) {} inline async_pipe(boost::asio::io_context & ios_source, @@ -45,8 +50,8 @@ class async_pipe inline async_pipe(const async_pipe& lhs); async_pipe(async_pipe&& lhs) : _source(std::move(lhs._source)), _sink(std::move(lhs._sink)) { - lhs._source.assign (-1); - lhs._sink .assign (-1); + lhs._source = ::boost::asio::posix::stream_descriptor{lhs._source.get_executor()}; + lhs._sink = ::boost::asio::posix::stream_descriptor{lhs._sink. get_executor()}; } template> From a60203dac33217607233d13e5b907df81fe5f342 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:32:48 +0800 Subject: [PATCH 009/397] Closes boostorg/process#121 --- include/boost/process/detail/posix/executor.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index 379e2c3f1..b44a20b47 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -273,15 +273,15 @@ class executor 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; + const auto * e = ::environ; while ((e != nullptr) && (*e != nullptr) && !boost::starts_with(*e, "PATH=")) e++; if ((e != nullptr) && (*e != nullptr)) { - *e += 5; //the beginnig of the string contains "PATH=" + auto p = e +5; //the beginning of the string contains "PATH=" std::vector path; - boost::split(path, *e, boost::is_any_of(":")); + boost::split(path, *p, boost::is_any_of(":")); for (const std::string & pp : path) { From 20b328dbf1a11ccc9fbb1c64cb50330ddd739a24 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:36:18 +0800 Subject: [PATCH 010/397] Attempting to fix wchar_t build error on circle. --- include/boost/process/detail/traits/wchar_t.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/traits/wchar_t.hpp b/include/boost/process/detail/traits/wchar_t.hpp index 98026d3e6..f509dd06d 100644 --- a/include/boost/process/detail/traits/wchar_t.hpp +++ b/include/boost/process/detail/traits/wchar_t.hpp @@ -7,13 +7,13 @@ #ifndef BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_ #define BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_ +#include + #include #include #include #include -#include - namespace boost { namespace process { namespace detail { //template From 0c3ded66367dd601cd7add761182cd2fe94b5c0e Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:47:53 +0800 Subject: [PATCH 011/397] Closes boostorg/process#197. --- .../process/detail/posix/sigchld_service.hpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/sigchld_service.hpp b/include/boost/process/detail/posix/sigchld_service.hpp index ac33e2f54..99d3f529b 100644 --- a/include/boost/process/detail/posix/sigchld_service.hpp +++ b/include/boost/process/detail/posix/sigchld_service.hpp @@ -48,9 +48,22 @@ class sigchld_service : public boost::asio::detail::service_base Date: Thu, 14 Oct 2021 15:23:03 +0800 Subject: [PATCH 012/397] Changed child(pid_t) signature. --- include/boost/process/detail/child_decl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/detail/child_decl.hpp b/include/boost/process/detail/child_decl.hpp index 48f02172d..3dcb777af 100644 --- a/include/boost/process/detail/child_decl.hpp +++ b/include/boost/process/detail/child_decl.hpp @@ -60,7 +60,7 @@ class child explicit child(child_handle &&ch, const std::shared_ptr> &ptr) : _child_handle(std::move(ch)), _exit_status(ptr) {} explicit child(child_handle &&ch) : _child_handle(std::move(ch)) {} - explicit child(const pid_t & pid) : _child_handle(pid), _attached(false) {}; + explicit child(pid_t pid) : _child_handle(pid), _attached(false) {}; child(const child&) = delete; child(child && lhs) noexcept : _child_handle(std::move(lhs._child_handle)), From 268795f3c07a24381caf4b263a7d2c8d5a9576ee Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 15:56:31 +0800 Subject: [PATCH 013/397] Multiple fixes. --- .../boost/process/detail/posix/executor.hpp | 4 ++-- .../boost/process/detail/posix/on_exit.hpp | 21 +++++++++++++++---- .../process/detail/posix/sigchld_service.hpp | 8 +++---- .../boost/process/detail/windows/on_exit.hpp | 12 +++++++++-- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index b44a20b47..f14879620 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -279,9 +279,9 @@ class executor if ((e != nullptr) && (*e != nullptr)) { - auto p = e +5; //the beginning of the string contains "PATH=" std::vector path; - boost::split(path, *p, boost::is_any_of(":")); + //the beginning of the string contains "PATH=" + boost::split(path, (*e) + 5, boost::is_any_of(":")); for (const std::string & pp : path) { diff --git a/include/boost/process/detail/posix/on_exit.hpp b/include/boost/process/detail/posix/on_exit.hpp index 0bac049c2..1dcac300c 100644 --- a/include/boost/process/detail/posix/on_exit.hpp +++ b/include/boost/process/detail/posix/on_exit.hpp @@ -6,26 +6,39 @@ #ifndef BOOST_PROCESS_POSIX_ON_EXIT_HPP_ #define BOOST_PROCESS_POSIX_ON_EXIT_HPP_ +#include +#include #include #include #include #include #include -namespace boost { namespace process { namespace detail { namespace posix { +namespace boost { namespace process { namespace detail { + +template +inline asio::io_context& get_io_context(const Tuple & tup); + +namespace posix { struct on_exit_ : boost::process::detail::posix::async_handler { std::function handler; on_exit_(const std::function & handler) : handler(handler) { - } template - std::function on_exit_handler(Executor&) + std::function on_exit_handler(Executor& exec) { - return handler; + auto v = boost::asio::prefer(boost::process::detail::get_io_context(exec.seq).get_executor(), + boost::asio::execution::outstanding_work.tracked); + auto handler_ = this->handler; + return + [handler_, v](int exit_code, const std::error_code & ec) + { + handler_(exit_code, ec); + }; } }; diff --git a/include/boost/process/detail/posix/sigchld_service.hpp b/include/boost/process/detail/posix/sigchld_service.hpp index 99d3f529b..7e92f569f 100644 --- a/include/boost/process/detail/posix/sigchld_service.hpp +++ b/include/boost/process/detail/posix/sigchld_service.hpp @@ -51,15 +51,15 @@ class sigchld_service : public boost::asio::detail::service_base #include #include #include #include #include -namespace boost { namespace process { namespace detail { namespace windows { +namespace boost { namespace process { namespace detail { + +template +inline asio::io_context& get_io_context(const Tuple & tup); + +namespace windows { struct on_exit_ : boost::process::detail::windows::async_handler { @@ -23,8 +29,10 @@ struct on_exit_ : boost::process::detail::windows::async_handler } template - std::function on_exit_handler(Executor&) + std::function on_exit_handler(Executor& exec) { + auto v = boost::asio::prefer(boost::process::detail::get_io_context(exec.seq).get_executor(), + boost::asio::execution::outstanding_work.tracked); auto handler_ = this->handler; return [handler_](int exit_code, const std::error_code & ec) { From cd4ef692e1c2ad53b7660f6f26cc47de8ef4be49 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 15 Oct 2021 10:54:38 +0800 Subject: [PATCH 014/397] Closes boostorg/process#189. --- test/async.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/async.cpp b/test/async.cpp index 08c688657..232b5b13c 100644 --- a/test/async.cpp +++ b/test/async.cpp @@ -182,8 +182,8 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(1 BOOST_REQUIRE(!ec); // Regression test for #143: make sure each io_context handles its own children - std::thread thr1{[&]{io_context1.run();}}; - std::thread thr2{[&]{io_context2.run();}}; + std::thread thr1{[&]() noexcept {io_context1.run();}}; + std::thread thr2{[&]() noexcept {io_context2.run();}}; thr1.join(); thr2.join(); From 9bb088ed5d850612f923effa4d7e444324c31ceb Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 15 Oct 2021 10:57:01 +0800 Subject: [PATCH 015/397] Closes boostorg/process#191. --- include/boost/process/detail/posix/executor.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index f14879620..864f8a023 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -152,11 +152,9 @@ class executor void write_error(const std::error_code & ec, const char * msg) { //I am the child - int len = ec.value(); - ::write(_pipe_sink, &len, sizeof(int)); + int data[2] = {ec.value(), std::strlen(msg) + 1}; - len = std::strlen(msg) + 1; - ::write(_pipe_sink, &len, sizeof(int)); + ::write(_pipe_sink, &data[0], sizeof(int) * 2); ::write(_pipe_sink, msg, len); } From 3acc1a3fa8fa42be66c7719774226849e2e758f6 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 15 Oct 2021 11:00:47 +0800 Subject: [PATCH 016/397] Added missing work guard on windows. --- include/boost/process/detail/posix/executor.hpp | 3 ++- include/boost/process/detail/windows/on_exit.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index 864f8a023..ca7713c13 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -152,7 +152,8 @@ class executor void write_error(const std::error_code & ec, const char * msg) { //I am the child - int data[2] = {ec.value(), std::strlen(msg) + 1}; + const auto len = std::strlen(msg); + int data[2] = {ec.value(), len + 1}; ::write(_pipe_sink, &data[0], sizeof(int) * 2); ::write(_pipe_sink, msg, len); diff --git a/include/boost/process/detail/windows/on_exit.hpp b/include/boost/process/detail/windows/on_exit.hpp index cba0bc6d8..10f3264bc 100644 --- a/include/boost/process/detail/windows/on_exit.hpp +++ b/include/boost/process/detail/windows/on_exit.hpp @@ -34,7 +34,7 @@ struct on_exit_ : boost::process::detail::windows::async_handler auto v = boost::asio::prefer(boost::process::detail::get_io_context(exec.seq).get_executor(), boost::asio::execution::outstanding_work.tracked); auto handler_ = this->handler; - return [handler_](int exit_code, const std::error_code & ec) + return [v, handler_](int exit_code, const std::error_code & ec) { handler_(static_cast(exit_code), ec); }; From 5ad5e825773a2a1631aefc2bbe3dc752a850e09a Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 22 Oct 2021 14:43:22 +0800 Subject: [PATCH 017/397] Trying to catch windows early complete. --- .../boost/process/detail/windows/io_context_ref.hpp | 12 +++++++++++- include/boost/process/detail/windows/is_running.hpp | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/boost/process/detail/windows/io_context_ref.hpp b/include/boost/process/detail/windows/io_context_ref.hpp index 783c3179e..506f00389 100644 --- a/include/boost/process/detail/windows/io_context_ref.hpp +++ b/include/boost/process/detail/windows/io_context_ref.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -114,6 +115,15 @@ struct io_context_ref : boost::process::detail::handler_base wait_handler wh(std::move(funcs), ios, process_handle, exec.exit_status); + ::boost::winapi::DWORD_ code; + if(::boost::winapi::GetExitCodeProcess(process_handle, &code) + && code != still_active) + { + ::boost::asio::post(wh.handle->get_executor(), std::move(wh)); + return; + } + + auto handle_p = wh.handle.get(); handle_p->async_wait(std::move(wh)); } @@ -135,7 +145,7 @@ struct io_context_ref : boost::process::detail::handler_base { } - void operator()(const boost::system::error_code & ec_in) + void operator()(const boost::system::error_code & ec_in = {}) { std::error_code ec; if (ec_in) diff --git a/include/boost/process/detail/windows/is_running.hpp b/include/boost/process/detail/windows/is_running.hpp index 93c5eb61e..f1115cdc6 100644 --- a/include/boost/process/detail/windows/is_running.hpp +++ b/include/boost/process/detail/windows/is_running.hpp @@ -7,6 +7,7 @@ #define BOOST_PROCESS_WINDOWS_IS_RUNNING_HPP #include +#include #include #include #include From 83380dad7911b254a9d3b53a95ae11b984b85859 Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 26 Oct 2021 23:53:02 +0800 Subject: [PATCH 018/397] Increased log level on windows. --- test/appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/test/appveyor.yml b/test/appveyor.yml index 10201399d..ea7c70450 100644 --- a/test/appveyor.yml +++ b/test/appveyor.yml @@ -13,6 +13,7 @@ init: - set BRANCH_TO_TEST=%APPVEYOR_REPO_BRANCH% - set BOOST_REMOVE=process + - set BOOST_TEST_LOG_LEVEL=success os: Visual Studio 2015 configuration: Debug From ed3b066da1cffffbfd7e6421e54f6a70f728621f Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 27 Oct 2021 11:43:33 +0800 Subject: [PATCH 019/397] Multiple windows test fixes --- .../process/detail/windows/io_context_ref.hpp | 3 +- include/boost/process/pipe.hpp | 2 +- test/async.cpp | 5 +-- test/limit_fd.cpp | 32 ++++++++++++++++--- test/test-file | 0 5 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 test/test-file diff --git a/include/boost/process/detail/windows/io_context_ref.hpp b/include/boost/process/detail/windows/io_context_ref.hpp index 783c3179e..ce6d432ab 100644 --- a/include/boost/process/detail/windows/io_context_ref.hpp +++ b/include/boost/process/detail/windows/io_context_ref.hpp @@ -130,7 +130,8 @@ struct io_context_ref : boost::process::detail::handler_base boost::asio::io_context & ios, void * handle, const std::shared_ptr> &exit_status) : funcs(std::move(funcs)), - handle(new boost::asio::windows::object_handle(ios.get_executor(), handle)), + handle(new boost::asio::windows::object_handle( + asio::prefer(ios.get_executor(), asio::execution::outstanding_work.tracked), handle)), exit_status(exit_status) { diff --git a/include/boost/process/pipe.hpp b/include/boost/process/pipe.hpp index 201d0fa62..24b261514 100644 --- a/include/boost/process/pipe.hpp +++ b/include/boost/process/pipe.hpp @@ -279,7 +279,7 @@ struct basic_pipebuf : std::basic_streambuf else if (wrt == 0) //broken pipe return false; - this->pbump(-wrt); + this->pbump(static_cast(-wrt)); return true; } diff --git a/test/async.cpp b/test/async.cpp index 08c688657..70db0afd5 100644 --- a/test/async.cpp +++ b/test/async.cpp @@ -214,7 +214,7 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(5)) int exit_code = 0; bp::child c( master_test_suite().argv[1], - "test", "--abort", + "test", "exit-code", "42", ec, io_context, bp::on_exit([&](int exit, const std::error_code& ec_in) @@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(5)) io_context.run(); BOOST_CHECK(exit_called); - BOOST_CHECK_NE(exit_code, 0); + BOOST_CHECK_NE(exit_code, 42); BOOST_CHECK_EQUAL(c.exit_code(), exit_code); } @@ -413,4 +413,5 @@ BOOST_AUTO_TEST_CASE(mixed_async, *boost::unit_test::timeout(5)) }*/ + BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/limit_fd.cpp b/test/limit_fd.cpp index fea9c1567..82e2efccd 100644 --- a/test/limit_fd.cpp +++ b/test/limit_fd.cpp @@ -163,18 +163,42 @@ BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5)) { #if defined(BOOST_WINDOWS_API) const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));}; + + // so it gets inherited by default + boost::winapi::SetHandleInformation(reinterpret_cast(_get_osfhandle(_fileno(stderr))), + boost::winapi::HANDLE_FLAG_INHERIT_, + boost::winapi::HANDLE_FLAG_INHERIT_); + #else const auto get_handle = [](FILE * f){return std::to_string(fileno(f));}; #endif + auto p = fopen("./test-file", "w"); + using boost::unit_test::framework::master_test_suite; + + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin)), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr)), EXIT_FAILURE); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin)), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(p)), EXIT_FAILURE); + + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin)), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr)), EXIT_FAILURE); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin)), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p)), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > stderr), EXIT_FAILURE); + + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr), bp::std_err > p), EXIT_FAILURE); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > p), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS); + fclose(p); } diff --git a/test/test-file b/test/test-file new file mode 100644 index 000000000..e69de29bb From e32651a2603abfb922755e27ec9cb85065952fb1 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 27 Oct 2021 12:23:18 +0800 Subject: [PATCH 020/397] Removed overly constraint tests. --- test/limit_fd.cpp | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/test/limit_fd.cpp b/test/limit_fd.cpp index 82e2efccd..c2807e939 100644 --- a/test/limit_fd.cpp +++ b/test/limit_fd.cpp @@ -163,12 +163,6 @@ BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5)) { #if defined(BOOST_WINDOWS_API) const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));}; - - // so it gets inherited by default - boost::winapi::SetHandleInformation(reinterpret_cast(_get_osfhandle(_fileno(stderr))), - boost::winapi::HANDLE_FLAG_INHERIT_, - boost::winapi::HANDLE_FLAG_INHERIT_); - #else const auto get_handle = [](FILE * f){return std::to_string(fileno(f));}; #endif @@ -177,28 +171,10 @@ BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5)) using boost::unit_test::framework::master_test_suite; - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin)), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr)), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin)), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(p)), EXIT_FAILURE); - - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin)), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr)), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin)), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p)), EXIT_FAILURE); - - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > stderr), EXIT_FAILURE); - - - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > p), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr), bp::std_err > p), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > p), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > p), EXIT_SUCCESS); - - + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_in < p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_out > p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p)), EXIT_FAILURE); fclose(p); } From d26ef5251950fb974dc3df5ad2a73477d20325d0 Mon Sep 17 00:00:00 2001 From: silent Date: Fri, 29 Oct 2021 12:18:20 +0300 Subject: [PATCH 021/397] fix missing headers --- include/boost/process/detail/windows/io_context_ref.hpp | 1 + include/boost/process/detail/windows/on_exit.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/boost/process/detail/windows/io_context_ref.hpp b/include/boost/process/detail/windows/io_context_ref.hpp index e2691b4b0..3722ff12a 100644 --- a/include/boost/process/detail/windows/io_context_ref.hpp +++ b/include/boost/process/detail/windows/io_context_ref.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/process/detail/windows/on_exit.hpp b/include/boost/process/detail/windows/on_exit.hpp index 10f3264bc..c98f8d09d 100644 --- a/include/boost/process/detail/windows/on_exit.hpp +++ b/include/boost/process/detail/windows/on_exit.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include From 5f80e72e9c7c5fa29f3bbb6ea40e0f95f192e490 Mon Sep 17 00:00:00 2001 From: silent Date: Fri, 29 Oct 2021 12:28:27 +0300 Subject: [PATCH 022/397] Closes klemens-morgenstern/boost-process#218 --- include/boost/process/detail/posix/environment.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/process/detail/posix/environment.hpp b/include/boost/process/detail/posix/environment.hpp index 5f297ed3f..c883f0c23 100644 --- a/include/boost/process/detail/posix/environment.hpp +++ b/include/boost/process/detail/posix/environment.hpp @@ -56,6 +56,7 @@ class native_environment_impl { _buffer = _load(); _impl = _load_var(_buffer); + _env_impl = _impl.data(); } string_type get(const pointer_type id) { return get(string_type(id)); } From a3e8600e40e0b68aa630805bf8b84eb69c4b3b8d Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Tue, 28 Dec 2021 19:41:40 -0300 Subject: [PATCH 023/397] Adjust docs `@boost` relative paths --- doc/Jamfile.jam | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Jamfile.jam b/doc/Jamfile.jam index e361b45e9..f1d506bbc 100644 --- a/doc/Jamfile.jam +++ b/doc/Jamfile.jam @@ -24,11 +24,12 @@ generators.register-standard common.copy : XML : XMLPROCESSWORKAROUND ; xmlprocessworkaround posix_pseudocode : posix_pseudocode.xml ; xmlprocessworkaround windows_pseudocode : windows_pseudocode.xml ; +path-constant INCLUDES : ../../.. ; doxygen autodoc : - ../../../boost/process.hpp - [ glob ../../../boost/process/*.hpp ] + $(INCLUDES)/boost/process.hpp + [ glob $(INCLUDES)/boost/process/*.hpp ] : EXCLUDE_SYMBOLS=BOOST_ASIO_INITFN_RESULT_TYPE PREDEFINED=BOOST_PROCESS_DOXYGEN @@ -41,7 +42,6 @@ doxygen autodoc ; - boostbook standalone : process.qbk From 4d1c438d915b0dd91774f86e757cebbf2663c472 Mon Sep 17 00:00:00 2001 From: hgkjshegfskef Date: Wed, 20 Apr 2022 17:36:20 +0200 Subject: [PATCH 024/397] Fix missing include --- include/boost/process/env.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/process/env.hpp b/include/boost/process/env.hpp index 108028312..47258c281 100644 --- a/include/boost/process/env.hpp +++ b/include/boost/process/env.hpp @@ -6,6 +6,7 @@ #ifndef BOOST_PROCESS_DETAIL_ENV_HPP_ #define BOOST_PROCESS_DETAIL_ENV_HPP_ +#include #include #include From ea26c7b2bd8d077069f4204907f659ddfb3e52b4 Mon Sep 17 00:00:00 2001 From: ikrijan <62850248+ikrijan@users.noreply.github.com> Date: Fri, 13 May 2022 16:50:25 +0200 Subject: [PATCH 025/397] Update executor.hpp explicit cast to int to silence this: `error: non-constant-expression cannot be narrowed from type 'unsigned long' to 'int' in initializer list [-Wc++11-narrowing]` --- include/boost/process/detail/posix/executor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index ca7713c13..fb2efedef 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -153,7 +153,7 @@ class executor { //I am the child const auto len = std::strlen(msg); - int data[2] = {ec.value(), len + 1}; + int data[2] = {ec.value(), static_cast(len + 1)}; ::write(_pipe_sink, &data[0], sizeof(int) * 2); ::write(_pipe_sink, msg, len); From edaf70a7a74b48ee2691c7d41552ebd91c5ae0c0 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:19:32 +0800 Subject: [PATCH 026/397] Removed unneeded WNOHANG. --- include/boost/process/detail/posix/terminate.hpp | 2 +- include/boost/process/detail/posix/wait_for_exit.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/terminate.hpp b/include/boost/process/detail/posix/terminate.hpp index e1e5f33fe..a707ea501 100644 --- a/include/boost/process/detail/posix/terminate.hpp +++ b/include/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, WNOHANG); //just to clean it up + ::waitpid(p.pid, &status, 0); //should not be WNOHANG, since that would allow zombies. } inline void terminate(const child_handle &p) diff --git a/include/boost/process/detail/posix/wait_for_exit.hpp b/include/boost/process/detail/posix/wait_for_exit.hpp index 376e48025..de8153b81 100644 --- a/include/boost/process/detail/posix/wait_for_exit.hpp +++ b/include/boost/process/detail/posix/wait_for_exit.hpp @@ -158,7 +158,7 @@ inline bool wait_until( { int res; ::kill(pid, SIGKILL); - ::waitpid(pid, &res, WNOHANG); + ::waitpid(pid, &res, 0); } }; child_cleaner_t child_cleaner{timeout_pid}; From d11e327ab0b99fd6b953de335c6deb422734d478 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:28:07 +0800 Subject: [PATCH 027/397] Closes boostorg/process#190 --- include/boost/process/detail/posix/async_pipe.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/async_pipe.hpp b/include/boost/process/detail/posix/async_pipe.hpp index 1766ce2f9..ea3bbe0ce 100644 --- a/include/boost/process/detail/posix/async_pipe.hpp +++ b/include/boost/process/detail/posix/async_pipe.hpp @@ -25,6 +25,11 @@ class async_pipe typedef ::boost::asio::posix::stream_descriptor handle_type; typedef typename handle_type::executor_type executor_type; + executor_type get_executor() + { + return _source.get_executor(); + } + inline async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios) {} inline async_pipe(boost::asio::io_context & ios_source, @@ -45,8 +50,8 @@ class async_pipe inline async_pipe(const async_pipe& lhs); async_pipe(async_pipe&& lhs) : _source(std::move(lhs._source)), _sink(std::move(lhs._sink)) { - lhs._source.assign (-1); - lhs._sink .assign (-1); + lhs._source = ::boost::asio::posix::stream_descriptor{lhs._source.get_executor()}; + lhs._sink = ::boost::asio::posix::stream_descriptor{lhs._sink. get_executor()}; } template> From 4a5d711c86eb9b77538b4eb28752189146c4731c Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:32:48 +0800 Subject: [PATCH 028/397] Closes boostorg/process#121 --- include/boost/process/detail/posix/executor.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index 379e2c3f1..b44a20b47 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -273,15 +273,15 @@ class executor 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; + const auto * e = ::environ; while ((e != nullptr) && (*e != nullptr) && !boost::starts_with(*e, "PATH=")) e++; if ((e != nullptr) && (*e != nullptr)) { - *e += 5; //the beginnig of the string contains "PATH=" + auto p = e +5; //the beginning of the string contains "PATH=" std::vector path; - boost::split(path, *e, boost::is_any_of(":")); + boost::split(path, *p, boost::is_any_of(":")); for (const std::string & pp : path) { From d54788a385067ab2e95e80415e18cef3eb6ed894 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:36:18 +0800 Subject: [PATCH 029/397] Attempting to fix wchar_t build error on circle. --- include/boost/process/detail/traits/wchar_t.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/traits/wchar_t.hpp b/include/boost/process/detail/traits/wchar_t.hpp index 98026d3e6..f509dd06d 100644 --- a/include/boost/process/detail/traits/wchar_t.hpp +++ b/include/boost/process/detail/traits/wchar_t.hpp @@ -7,13 +7,13 @@ #ifndef BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_ #define BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_ +#include + #include #include #include #include -#include - namespace boost { namespace process { namespace detail { //template From e358dc52a2c0f866cb9d0efad001da74a4790b2d Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 14:47:53 +0800 Subject: [PATCH 030/397] Closes boostorg/process#197. --- .../process/detail/posix/sigchld_service.hpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/sigchld_service.hpp b/include/boost/process/detail/posix/sigchld_service.hpp index ac33e2f54..99d3f529b 100644 --- a/include/boost/process/detail/posix/sigchld_service.hpp +++ b/include/boost/process/detail/posix/sigchld_service.hpp @@ -48,9 +48,22 @@ class sigchld_service : public boost::asio::detail::service_base Date: Thu, 14 Oct 2021 15:23:03 +0800 Subject: [PATCH 031/397] Changed child(pid_t) signature. --- include/boost/process/detail/child_decl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/detail/child_decl.hpp b/include/boost/process/detail/child_decl.hpp index 48f02172d..3dcb777af 100644 --- a/include/boost/process/detail/child_decl.hpp +++ b/include/boost/process/detail/child_decl.hpp @@ -60,7 +60,7 @@ class child explicit child(child_handle &&ch, const std::shared_ptr> &ptr) : _child_handle(std::move(ch)), _exit_status(ptr) {} explicit child(child_handle &&ch) : _child_handle(std::move(ch)) {} - explicit child(const pid_t & pid) : _child_handle(pid), _attached(false) {}; + explicit child(pid_t pid) : _child_handle(pid), _attached(false) {}; child(const child&) = delete; child(child && lhs) noexcept : _child_handle(std::move(lhs._child_handle)), From 5abb4f4a2305bf506daff5463d95a4c181f55b65 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 14 Oct 2021 15:56:31 +0800 Subject: [PATCH 032/397] Multiple fixes. --- .../boost/process/detail/posix/executor.hpp | 4 ++-- .../boost/process/detail/posix/on_exit.hpp | 21 +++++++++++++++---- .../process/detail/posix/sigchld_service.hpp | 8 +++---- .../boost/process/detail/windows/on_exit.hpp | 12 +++++++++-- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index b44a20b47..f14879620 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -279,9 +279,9 @@ class executor if ((e != nullptr) && (*e != nullptr)) { - auto p = e +5; //the beginning of the string contains "PATH=" std::vector path; - boost::split(path, *p, boost::is_any_of(":")); + //the beginning of the string contains "PATH=" + boost::split(path, (*e) + 5, boost::is_any_of(":")); for (const std::string & pp : path) { diff --git a/include/boost/process/detail/posix/on_exit.hpp b/include/boost/process/detail/posix/on_exit.hpp index 0bac049c2..1dcac300c 100644 --- a/include/boost/process/detail/posix/on_exit.hpp +++ b/include/boost/process/detail/posix/on_exit.hpp @@ -6,26 +6,39 @@ #ifndef BOOST_PROCESS_POSIX_ON_EXIT_HPP_ #define BOOST_PROCESS_POSIX_ON_EXIT_HPP_ +#include +#include #include #include #include #include #include -namespace boost { namespace process { namespace detail { namespace posix { +namespace boost { namespace process { namespace detail { + +template +inline asio::io_context& get_io_context(const Tuple & tup); + +namespace posix { struct on_exit_ : boost::process::detail::posix::async_handler { std::function handler; on_exit_(const std::function & handler) : handler(handler) { - } template - std::function on_exit_handler(Executor&) + std::function on_exit_handler(Executor& exec) { - return handler; + auto v = boost::asio::prefer(boost::process::detail::get_io_context(exec.seq).get_executor(), + boost::asio::execution::outstanding_work.tracked); + auto handler_ = this->handler; + return + [handler_, v](int exit_code, const std::error_code & ec) + { + handler_(exit_code, ec); + }; } }; diff --git a/include/boost/process/detail/posix/sigchld_service.hpp b/include/boost/process/detail/posix/sigchld_service.hpp index 99d3f529b..7e92f569f 100644 --- a/include/boost/process/detail/posix/sigchld_service.hpp +++ b/include/boost/process/detail/posix/sigchld_service.hpp @@ -51,15 +51,15 @@ class sigchld_service : public boost::asio::detail::service_base #include #include #include #include #include -namespace boost { namespace process { namespace detail { namespace windows { +namespace boost { namespace process { namespace detail { + +template +inline asio::io_context& get_io_context(const Tuple & tup); + +namespace windows { struct on_exit_ : boost::process::detail::windows::async_handler { @@ -23,8 +29,10 @@ struct on_exit_ : boost::process::detail::windows::async_handler } template - std::function on_exit_handler(Executor&) + std::function on_exit_handler(Executor& exec) { + auto v = boost::asio::prefer(boost::process::detail::get_io_context(exec.seq).get_executor(), + boost::asio::execution::outstanding_work.tracked); auto handler_ = this->handler; return [handler_](int exit_code, const std::error_code & ec) { From 1f464b3eb55b61c7c357fe709c2d9a0e63ca1fbe Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 15 Oct 2021 10:54:38 +0800 Subject: [PATCH 033/397] Closes boostorg/process#189. --- test/async.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/async.cpp b/test/async.cpp index 08c688657..232b5b13c 100644 --- a/test/async.cpp +++ b/test/async.cpp @@ -182,8 +182,8 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(1 BOOST_REQUIRE(!ec); // Regression test for #143: make sure each io_context handles its own children - std::thread thr1{[&]{io_context1.run();}}; - std::thread thr2{[&]{io_context2.run();}}; + std::thread thr1{[&]() noexcept {io_context1.run();}}; + std::thread thr2{[&]() noexcept {io_context2.run();}}; thr1.join(); thr2.join(); From b7821ccf09b48153bb340bb9e4a63d897e443509 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 15 Oct 2021 10:57:01 +0800 Subject: [PATCH 034/397] Closes boostorg/process#191. --- include/boost/process/detail/posix/executor.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index f14879620..864f8a023 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -152,11 +152,9 @@ class executor void write_error(const std::error_code & ec, const char * msg) { //I am the child - int len = ec.value(); - ::write(_pipe_sink, &len, sizeof(int)); + int data[2] = {ec.value(), std::strlen(msg) + 1}; - len = std::strlen(msg) + 1; - ::write(_pipe_sink, &len, sizeof(int)); + ::write(_pipe_sink, &data[0], sizeof(int) * 2); ::write(_pipe_sink, msg, len); } From 221550a848bff0ecb16b3fd2790638ab7a0bdda5 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 15 Oct 2021 11:00:47 +0800 Subject: [PATCH 035/397] Added missing work guard on windows. --- include/boost/process/detail/posix/executor.hpp | 3 ++- include/boost/process/detail/windows/on_exit.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index 864f8a023..ca7713c13 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -152,7 +152,8 @@ class executor void write_error(const std::error_code & ec, const char * msg) { //I am the child - int data[2] = {ec.value(), std::strlen(msg) + 1}; + const auto len = std::strlen(msg); + int data[2] = {ec.value(), len + 1}; ::write(_pipe_sink, &data[0], sizeof(int) * 2); ::write(_pipe_sink, msg, len); diff --git a/include/boost/process/detail/windows/on_exit.hpp b/include/boost/process/detail/windows/on_exit.hpp index cba0bc6d8..10f3264bc 100644 --- a/include/boost/process/detail/windows/on_exit.hpp +++ b/include/boost/process/detail/windows/on_exit.hpp @@ -34,7 +34,7 @@ struct on_exit_ : boost::process::detail::windows::async_handler auto v = boost::asio::prefer(boost::process::detail::get_io_context(exec.seq).get_executor(), boost::asio::execution::outstanding_work.tracked); auto handler_ = this->handler; - return [handler_](int exit_code, const std::error_code & ec) + return [v, handler_](int exit_code, const std::error_code & ec) { handler_(static_cast(exit_code), ec); }; From ee3c2cfeeb01ff01af8bc82926b7028c6f7b735d Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 22 Oct 2021 14:43:22 +0800 Subject: [PATCH 036/397] Trying to catch windows early complete. --- .../boost/process/detail/windows/io_context_ref.hpp | 12 +++++++++++- include/boost/process/detail/windows/is_running.hpp | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/boost/process/detail/windows/io_context_ref.hpp b/include/boost/process/detail/windows/io_context_ref.hpp index 783c3179e..506f00389 100644 --- a/include/boost/process/detail/windows/io_context_ref.hpp +++ b/include/boost/process/detail/windows/io_context_ref.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -114,6 +115,15 @@ struct io_context_ref : boost::process::detail::handler_base wait_handler wh(std::move(funcs), ios, process_handle, exec.exit_status); + ::boost::winapi::DWORD_ code; + if(::boost::winapi::GetExitCodeProcess(process_handle, &code) + && code != still_active) + { + ::boost::asio::post(wh.handle->get_executor(), std::move(wh)); + return; + } + + auto handle_p = wh.handle.get(); handle_p->async_wait(std::move(wh)); } @@ -135,7 +145,7 @@ struct io_context_ref : boost::process::detail::handler_base { } - void operator()(const boost::system::error_code & ec_in) + void operator()(const boost::system::error_code & ec_in = {}) { std::error_code ec; if (ec_in) diff --git a/include/boost/process/detail/windows/is_running.hpp b/include/boost/process/detail/windows/is_running.hpp index 93c5eb61e..f1115cdc6 100644 --- a/include/boost/process/detail/windows/is_running.hpp +++ b/include/boost/process/detail/windows/is_running.hpp @@ -7,6 +7,7 @@ #define BOOST_PROCESS_WINDOWS_IS_RUNNING_HPP #include +#include #include #include #include From 220bec28bfe6d246e7845b8084894f8a1b2ba899 Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 26 Oct 2021 23:53:02 +0800 Subject: [PATCH 037/397] Increased log level on windows. --- test/appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/test/appveyor.yml b/test/appveyor.yml index 10201399d..ea7c70450 100644 --- a/test/appveyor.yml +++ b/test/appveyor.yml @@ -13,6 +13,7 @@ init: - set BRANCH_TO_TEST=%APPVEYOR_REPO_BRANCH% - set BOOST_REMOVE=process + - set BOOST_TEST_LOG_LEVEL=success os: Visual Studio 2015 configuration: Debug From 4cadf1d333c041eb1a0b9e09345e3368202603e6 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 27 Oct 2021 11:43:33 +0800 Subject: [PATCH 038/397] Multiple windows test fixes --- .../process/detail/windows/io_context_ref.hpp | 3 +- include/boost/process/pipe.hpp | 2 +- test/async.cpp | 5 +-- test/limit_fd.cpp | 32 ++++++++++++++++--- test/test-file | 0 5 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 test/test-file diff --git a/include/boost/process/detail/windows/io_context_ref.hpp b/include/boost/process/detail/windows/io_context_ref.hpp index 506f00389..e2691b4b0 100644 --- a/include/boost/process/detail/windows/io_context_ref.hpp +++ b/include/boost/process/detail/windows/io_context_ref.hpp @@ -140,7 +140,8 @@ struct io_context_ref : boost::process::detail::handler_base boost::asio::io_context & ios, void * handle, const std::shared_ptr> &exit_status) : funcs(std::move(funcs)), - handle(new boost::asio::windows::object_handle(ios.get_executor(), handle)), + handle(new boost::asio::windows::object_handle( + asio::prefer(ios.get_executor(), asio::execution::outstanding_work.tracked), handle)), exit_status(exit_status) { diff --git a/include/boost/process/pipe.hpp b/include/boost/process/pipe.hpp index 201d0fa62..24b261514 100644 --- a/include/boost/process/pipe.hpp +++ b/include/boost/process/pipe.hpp @@ -279,7 +279,7 @@ struct basic_pipebuf : std::basic_streambuf else if (wrt == 0) //broken pipe return false; - this->pbump(-wrt); + this->pbump(static_cast(-wrt)); return true; } diff --git a/test/async.cpp b/test/async.cpp index 232b5b13c..e12f4bf53 100644 --- a/test/async.cpp +++ b/test/async.cpp @@ -214,7 +214,7 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(5)) int exit_code = 0; bp::child c( master_test_suite().argv[1], - "test", "--abort", + "test", "exit-code", "42", ec, io_context, bp::on_exit([&](int exit, const std::error_code& ec_in) @@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(5)) io_context.run(); BOOST_CHECK(exit_called); - BOOST_CHECK_NE(exit_code, 0); + BOOST_CHECK_NE(exit_code, 42); BOOST_CHECK_EQUAL(c.exit_code(), exit_code); } @@ -413,4 +413,5 @@ BOOST_AUTO_TEST_CASE(mixed_async, *boost::unit_test::timeout(5)) }*/ + BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/limit_fd.cpp b/test/limit_fd.cpp index fea9c1567..82e2efccd 100644 --- a/test/limit_fd.cpp +++ b/test/limit_fd.cpp @@ -163,18 +163,42 @@ BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5)) { #if defined(BOOST_WINDOWS_API) const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));}; + + // so it gets inherited by default + boost::winapi::SetHandleInformation(reinterpret_cast(_get_osfhandle(_fileno(stderr))), + boost::winapi::HANDLE_FLAG_INHERIT_, + boost::winapi::HANDLE_FLAG_INHERIT_); + #else const auto get_handle = [](FILE * f){return std::to_string(fileno(f));}; #endif + auto p = fopen("./test-file", "w"); + using boost::unit_test::framework::master_test_suite; + + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin)), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr)), EXIT_FAILURE); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin)), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(p)), EXIT_FAILURE); + + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin)), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr)), EXIT_FAILURE); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin)), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p)), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > stderr), EXIT_FAILURE); + + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr), bp::std_err > p), EXIT_FAILURE); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > p), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS); + fclose(p); } diff --git a/test/test-file b/test/test-file new file mode 100644 index 000000000..e69de29bb From ed659bf12936f856bc35bd14bbc0554f47c25723 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Wed, 27 Oct 2021 12:23:18 +0800 Subject: [PATCH 039/397] Removed overly constraint tests. --- test/limit_fd.cpp | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/test/limit_fd.cpp b/test/limit_fd.cpp index 82e2efccd..c2807e939 100644 --- a/test/limit_fd.cpp +++ b/test/limit_fd.cpp @@ -163,12 +163,6 @@ BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5)) { #if defined(BOOST_WINDOWS_API) const auto get_handle = [](FILE * f){return std::to_string(_get_osfhandle(_fileno(f)));}; - - // so it gets inherited by default - boost::winapi::SetHandleInformation(reinterpret_cast(_get_osfhandle(_fileno(stderr))), - boost::winapi::HANDLE_FLAG_INHERIT_, - boost::winapi::HANDLE_FLAG_INHERIT_); - #else const auto get_handle = [](FILE * f){return std::to_string(fileno(f));}; #endif @@ -177,28 +171,10 @@ BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5)) using boost::unit_test::framework::master_test_suite; - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin)), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr)), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdin)), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(p)), EXIT_FAILURE); - - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin)), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr)), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin)), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p)), EXIT_FAILURE); - - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > stderr), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > stderr), EXIT_FAILURE); - - - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > p), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stderr), bp::std_err > p), EXIT_FAILURE); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(stdin), bp::std_err > p), EXIT_SUCCESS); - BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > p), EXIT_SUCCESS); - - + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_in < p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_out > p), EXIT_SUCCESS); + BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p)), EXIT_FAILURE); fclose(p); } From b8bcfa2e11148e60c43f55a8922a917e52d6cd34 Mon Sep 17 00:00:00 2001 From: silent Date: Fri, 29 Oct 2021 12:18:20 +0300 Subject: [PATCH 040/397] fix missing headers --- include/boost/process/detail/windows/io_context_ref.hpp | 1 + include/boost/process/detail/windows/on_exit.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/boost/process/detail/windows/io_context_ref.hpp b/include/boost/process/detail/windows/io_context_ref.hpp index e2691b4b0..3722ff12a 100644 --- a/include/boost/process/detail/windows/io_context_ref.hpp +++ b/include/boost/process/detail/windows/io_context_ref.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/process/detail/windows/on_exit.hpp b/include/boost/process/detail/windows/on_exit.hpp index 10f3264bc..c98f8d09d 100644 --- a/include/boost/process/detail/windows/on_exit.hpp +++ b/include/boost/process/detail/windows/on_exit.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include From a295cd863587f23a4f6ac2f523b76c24fe84ce57 Mon Sep 17 00:00:00 2001 From: silent Date: Fri, 29 Oct 2021 12:28:27 +0300 Subject: [PATCH 041/397] Closes klemens-morgenstern/boost-process#218 --- include/boost/process/detail/posix/environment.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/process/detail/posix/environment.hpp b/include/boost/process/detail/posix/environment.hpp index 5f297ed3f..c883f0c23 100644 --- a/include/boost/process/detail/posix/environment.hpp +++ b/include/boost/process/detail/posix/environment.hpp @@ -56,6 +56,7 @@ class native_environment_impl { _buffer = _load(); _impl = _load_var(_buffer); + _env_impl = _impl.data(); } string_type get(const pointer_type id) { return get(string_type(id)); } From ab82e78c3db2b472356642657500f7a4b5a2857e Mon Sep 17 00:00:00 2001 From: ikrijan <62850248+ikrijan@users.noreply.github.com> Date: Fri, 13 May 2022 16:50:25 +0200 Subject: [PATCH 042/397] Update executor.hpp explicit cast to int to silence this: `error: non-constant-expression cannot be narrowed from type 'unsigned long' to 'int' in initializer list [-Wc++11-narrowing]` --- include/boost/process/detail/posix/executor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index ca7713c13..fb2efedef 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -153,7 +153,7 @@ class executor { //I am the child const auto len = std::strlen(msg); - int data[2] = {ec.value(), len + 1}; + int data[2] = {ec.value(), static_cast(len + 1)}; ::write(_pipe_sink, &data[0], sizeof(int) * 2); ::write(_pipe_sink, msg, len); From 610b337fa3bc82ecd19cd7baa6e90874f62c050f Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Thu, 19 May 2022 17:00:15 +0800 Subject: [PATCH 043/397] Drone & Multple fix (#250) Squash after invalid branch & merge conflict. * Fixed file_descriptor move assignment operator to return a reference to 'this'. Issue # 219 * Returning *this instead of erroneous *this. Issue # 219 * Removed unneeded WNOHANG. * Closes boostorg/process#190 * Closes boostorg/process#121 * Attempting to fix wchar_t build error on circle. * Closes boostorg/process#197. * Changed child(pid_t) signature. * Multiple fixes. * Closes boostorg/process#189. * Closes boostorg/process#191. * Added missing work guard on windows. * Trying to catch windows early complete. * Increased log level on windows. * Multiple windows test fixes * Removed overly constraint tests. * fix missing headers * Closes klemens-morgenstern/boost-process#218 * Update executor.hpp explicit cast to int to silence this: `error: non-constant-expression cannot be narrowed from type 'unsigned long' to 'int' in initializer list [-Wc++11-narrowing]` * Fix posix implementation of move constructor/assignment in file_descriptor * Adjust docs `@boost` relative paths * Fixed UB for large environment names. * Closes boostorg/process#207. * Drone setup * Added include for filesystem::fstream. * Disabled useless tests. * Fixed environment length checks. * Pipe test & warning fixes. * Disabled warnings & added windows include fix. * More test fixes. * Removed some tests from apple build. * Removed some tests from apple build. * Disabled OSX tests via build script & fixed windows examples. * TSA fix attempt. Co-authored-by: James Baker Co-authored-by: silent Co-authored-by: ikrijan <62850248+ikrijan@users.noreply.github.com> Co-authored-by: Shauren Co-authored-by: alandefreitas --- .drone.star | 60 ++++++ .drone/drone.bat | 36 ++++ .drone/drone.sh | 199 ++++++++++++++++++ CMakeLists.txt | 3 +- doc/tutorial.qbk | 6 +- example/io.cpp | 2 +- example/start_dir.cpp | 6 +- example/windows.cpp | 5 +- include/boost/process/detail/basic_cmd.hpp | 2 +- .../boost/process/detail/posix/basic_pipe.hpp | 4 +- .../boost/process/detail/posix/executor.hpp | 6 +- .../process/detail/posix/file_descriptor.hpp | 4 +- .../process/detail/posix/search_path.hpp | 12 +- .../boost/process/detail/posix/shell_path.hpp | 6 +- .../boost/process/detail/posix/start_dir.hpp | 3 +- .../process/detail/traits/cmd_or_exe.hpp | 8 +- .../boost/process/detail/traits/wchar_t.hpp | 2 +- .../process/detail/windows/environment.hpp | 2 + .../detail/windows/file_descriptor.hpp | 4 +- .../boost/process/detail/windows/handles.hpp | 1 + .../process/detail/windows/search_path.hpp | 17 +- .../process/detail/windows/shell_path.hpp | 10 +- include/boost/process/environment.hpp | 18 +- include/boost/process/exe.hpp | 10 +- include/boost/process/filesystem.hpp | 28 +++ include/boost/process/io.hpp | 18 +- include/boost/process/search_path.hpp | 4 +- include/boost/process/shell.hpp | 6 +- include/boost/process/start_dir.hpp | 12 +- test/CMakeLists.txt | 74 +++++++ test/Jamfile.jam | 8 +- test/args_handling.cpp | 6 +- test/async.cpp | 11 +- test/async_pipe.cpp | 4 +- test/bind_stderr.cpp | 7 +- test/bind_stdin.cpp | 13 +- test/bind_stdin_stdout.cpp | 1 + test/bind_stdout.cpp | 7 +- test/bind_stdout_stderr.cpp | 1 + test/cmd_test.cpp | 4 +- test/group_wait.cpp | 10 +- test/limit_fd.cpp | 13 +- test/pipe.cpp | 3 +- test/posix_specific.cpp | 4 +- test/run_exe_path.cpp | 6 +- test/search_path.cpp | 4 +- test/shell.cpp | 5 + test/shell_path.cpp | 8 +- test/sparring_partner.cpp | 4 +- test/spawn.cpp | 4 +- test/spawn_fail.cpp | 4 +- test/start_dir.cpp | 12 +- test/sub_launcher.cpp | 2 +- test/system_test1.cpp | 4 +- test/system_test2.cpp | 4 +- test/wait_for.cpp | 4 +- 56 files changed, 577 insertions(+), 144 deletions(-) create mode 100644 .drone.star create mode 100755 .drone/drone.bat create mode 100755 .drone/drone.sh create mode 100644 include/boost/process/filesystem.hpp create mode 100644 test/CMakeLists.txt diff --git a/.drone.star b/.drone.star new file mode 100644 index 000000000..3411f507a --- /dev/null +++ b/.drone.star @@ -0,0 +1,60 @@ +# Use, modification, and distribution are +# subject to the Boost Software License, Version 1.0. (See accompanying +# file LICENSE.txt) +# +# Copyright Rene Rivera 2020. + +# For Drone CI we use the Starlark scripting language to reduce duplication. +# As the yaml syntax for Drone CI is rather limited. +# +# +globalenv={'B2_CI_VERSION': '1', 'B2_VARIANT': 'release'} +linuxglobalimage="cppalliance/droneubuntu1804:1" +windowsglobalimage="cppalliance/dronevs2019" + +def main(ctx): + return [ + linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), + # A set of jobs based on the earlier .travis.yml configuration: + linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb mlocate", image="cppalliance/droneubuntu2004:1", buildtype="boost_v1", buildscript="drone", environment={"GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "14", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), + linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={ "B2_TOOLSET": "clang-7", "B2_CXXSTD": "17,2a", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "11", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), + linux_cxx("GCC Valgrind", "g++", packages="g++-7 libssl-dev valgrind mlocate", image="cppalliance/droneubuntu2004:1", buildtype="boost_v1", buildscript="drone", environment={ "VARIANT": "process_valgrind", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), + linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), + linux_cxx("GCC 8, C++17, libstdc++, release", "g++-8", packages="g++-8 mlocate", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++-8", "CXXSTD" : "17" }, globalenv=globalenv), + linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={"VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "11", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), + linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv), + # Next, a standard list of tests from boost-ci: + linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), + linux_cxx("tsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'tsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_TSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_JOB_UUID': 'da4b9237ba'}, globalenv=globalenv), + linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), + # a boost-ci based version of codecov. However, codecov has already been run, above. + # linux_cxx("codecov", "g++-8", packages="g++-8", buildtype="codecov", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'codecov.io', 'B2_CXXSTD': '11', 'B2_TOOLSET': 'gcc-8', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_JOB_UUID': 'b6589fc6ab', "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv), + # gcc 4.8 is failing: + # # linux_cxx("gcc 4.8", "g++-4.8", packages="g++-4.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={'B2_TOOLSET': 'gcc-4.8', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': 'ac3478d69a'}, globalenv=globalenv), + linux_cxx("gcc 5", "g++-5", packages="g++-5", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-5', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), + linux_cxx("gcc 6", "g++-6", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-6', 'B2_CXXSTD': '11,14', 'DRONE_JOB_UUID': 'fe5dbbcea5'}, globalenv=globalenv), + # # linux_cxx("gcc 7", "g++-7", packages="g++-7", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-7', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '0ade7c2cf9'}, globalenv=globalenv), + # # linux_cxx("gcc 8", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': 'b1d5781111'}, globalenv=globalenv), + # # linux_cxx("gcc 9", "g++-9", packages="g++-9", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-9', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079149'}, globalenv=globalenv), + # # linux_cxx("gcc 10", "g++-10", packages="g++-10", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'gcc-10', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079159'}, globalenv=globalenv), + linux_cxx("gcc 11", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079169'}, globalenv=globalenv), + linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), + # # linux_cxx("clang 4.0", "clang++-4.0", packages="clang-4.0 libstdc++-6-dev", llvm_os="xenial", llvm_ver="4.0", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-4.0', 'B2_CXXSTD': '11,14', 'DRONE_JOB_UUID': 'bd307a3ec3'}, globalenv=globalenv), + # # linux_cxx("clang 5.0", "clang++-5.0", packages="clang-5.0 libstdc++-7-dev", llvm_os="bionic", llvm_ver="5.0", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-5.0', 'B2_CXXSTD': '11,14', 'DRONE_JOB_UUID': 'fa35e19212'}, globalenv=globalenv), + # # linux_cxx("clang 6.0", "clang++-6.0", packages="clang-6.0 libc6-dbg libc++-dev libstdc++-8-dev", llvm_os="bionic", llvm_ver="6.0", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-6.0', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': 'f1abd67035'}, globalenv=globalenv), + # # linux_cxx("clang 7", "clang++-7", packages="clang-7 libc6-dbg libc++-dev libstdc++-8-dev", llvm_os="bionic", llvm_ver="7", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-7', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '1574bddb75'}, globalenv=globalenv), + # # linux_cxx("clang 8", "clang++-8", packages="clang-8 libc6-dbg libc++-dev libstdc++-8-dev", llvm_os="bionic", llvm_ver="8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-8', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '0716d9708d'}, globalenv=globalenv), + # # linux_cxx("clang 9", "clang++-9", packages="clang-9 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="bionic", llvm_ver="9", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'B2_CXXSTD': '11,14,17,2a', 'DRONE_JOB_UUID': '9e6a55b6b4'}, globalenv=globalenv), + # # linux_cxx("clang 10", "clang++-10", packages="clang-10 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="focal", llvm_ver="10", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-10', 'B2_CXXSTD': '11,14,17,2a', 'DRONE_JOB_UUID': '9e6a55b6c4'}, globalenv=globalenv), + # # linux_cxx("clang 11", "clang++-11", packages="clang-11 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="focal", llvm_ver="11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-11', 'B2_CXXSTD': '11,14,17,2a', 'DRONE_JOB_UUID': '9e6a55b6b4'}, globalenv=globalenv), + linux_cxx("clang 12", "clang++-12", packages="clang-12 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="focal", llvm_ver="12", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-12', 'B2_CXXSTD': '11,14,17,20', 'DRONE_JOB_UUID': '9e6a55b6b5'}, globalenv=globalenv), + # # linux_cxx("clang 6.0 libc++", "clang++-6.0", packages="clang-6.0 libc6-dbg libc++-dev libstdc++-8-dev libc++abi-dev", llvm_os="bionic", llvm_ver="6.0", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-6.0', 'B2_CXXSTD': '11,14', 'B2_STDLIB': 'libc++', 'DRONE_JOB_UUID': 'b3f0c7f6bb'}, globalenv=globalenv), + osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={'B2_TOOLSET': 'clang', 'B2_CXXSTD': '11,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), + linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), + windows_cxx("msvc-14.1", "", image="cppalliance/dronevs2017", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "msvc-14.1", "CXXSTD": "17", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), + # # windows_cxx("msvc-14.2", "", image="cppalliance/dronevs2019", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "msvc-14.2", "CXXSTD": "17", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), + windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "20", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), + ] + +# from https://github.com/boostorg/boost-ci +load("@boost_ci//ci/drone/:functions.star", "linux_cxx","windows_cxx","osx_cxx","freebsd_cxx") diff --git a/.drone/drone.bat b/.drone/drone.bat new file mode 100755 index 000000000..0eb2236e3 --- /dev/null +++ b/.drone/drone.bat @@ -0,0 +1,36 @@ +@ECHO ON +setlocal enabledelayedexpansion + +if "%DRONE_JOB_BUILDTYPE%" == "boost" ( + +echo '==================================> INSTALL' + +git clone https://github.com/boostorg/boost-ci.git boost-ci-cloned --depth 1 +cp -prf boost-ci-cloned/ci . +rm -rf boost-ci-cloned + +REM source ci/travis/install.sh +REM The contents of install.sh below: + +for /F %%i in ("%DRONE_REPO%") do @set SELF=%%~nxi +SET BOOST_CI_TARGET_BRANCH=%DRONE_COMMIT_BRANCH% +SET BOOST_CI_SRC_FOLDER=%cd% + +call ci\common_install.bat + +echo '==================================> COMPILE' + +REM set B2_TARGETS=libs/!SELF!/test libs/!SELF!/example +set B2_TARGETS=libs/!SELF!/test + +cd !BOOST_ROOT! +call bootstrap.bat +b2 headers +b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% define=%DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/test -j3 +b2 --debug-configuration variant=%VARIANT% cxxstd=%CXXSTD% define=%DEFINE% address-model=%ADDRESS_MODEL% toolset=%TOOLSET% --verbose-test libs/!SELF!/example -j3 + +) else if "%DRONE_JOB_BUILDTYPE%" == "standalone-windows" ( + +REM not used + +) \ No newline at end of file diff --git a/.drone/drone.sh b/.drone/drone.sh new file mode 100755 index 000000000..ed3bfae07 --- /dev/null +++ b/.drone/drone.sh @@ -0,0 +1,199 @@ +#!/bin/bash + +# Copyright 2020 Rene Rivera, Sam Darwin +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at http://boost.org/LICENSE_1_0.txt) + +set -xe + +export TRAVIS_BUILD_DIR=$(pwd) +export DRONE_BUILD_DIR=$(pwd) +export TRAVIS_BRANCH=$DRONE_BRANCH +export TRAVIS_EVENT_TYPE=$DRONE_BUILD_EVENT +export VCS_COMMIT_ID=$DRONE_COMMIT +export GIT_COMMIT=$DRONE_COMMIT +export REPO_NAME=$DRONE_REPO +export USER=$(whoami) +export CC=${CC:-gcc} +export PATH=~/.local/bin:/usr/local/bin:$PATH + +common_install () { + git clone https://github.com/boostorg/boost-ci.git boost-ci-cloned --depth 1 + cp -prf boost-ci-cloned/ci . + rm -rf boost-ci-cloned + + if [ "$TRAVIS_OS_NAME" == "osx" ]; then + unset -f cd + fi + + export SELF=`basename $REPO_NAME` + export BOOST_CI_TARGET_BRANCH="$TRAVIS_BRANCH" + export BOOST_CI_SRC_FOLDER=$(pwd) + + . ./ci/common_install.sh +} + +if [ "$DRONE_JOB_BUILDTYPE" == "boost" ]; then + +echo '==================================> INSTALL' + +common_install + +echo '==================================> SCRIPT' + +$BOOST_ROOT/libs/$SELF/ci/travis/build.sh + +elif [ "$DRONE_JOB_BUILDTYPE" == "docs" ]; then + +echo '==================================> INSTALL' + +export SELF=`basename $REPO_NAME` + +pwd +cd .. +mkdir -p $HOME/cache && cd $HOME/cache +if [ ! -d doxygen ]; then git clone -b 'Release_1_8_15' --depth 1 https://github.com/doxygen/doxygen.git && echo "not-cached" ; else echo "cached" ; fi +cd doxygen +cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release +cd build +sudo make install +cd ../.. +if [ ! -f saxonhe.zip ]; then wget -O saxonhe.zip https://sourceforge.net/projects/saxon/files/Saxon-HE/9.9/SaxonHE9-9-1-4J.zip/download && echo "not-cached" ; else echo "cached" ; fi +unzip -o saxonhe.zip +sudo rm /usr/share/java/Saxon-HE.jar +sudo cp saxon9he.jar /usr/share/java/Saxon-HE.jar +cd .. +BOOST_BRANCH=develop && [ "$TRAVIS_BRANCH" == "master" ] && BOOST_BRANCH=master || true +git clone -b $BOOST_BRANCH https://github.com/boostorg/boost.git boost-root --depth 1 +cd boost-root +export BOOST_ROOT=$(pwd) +git submodule update --init libs/context +git submodule update --init tools/boostbook +git submodule update --init tools/boostdep +git submodule update --init tools/docca +git submodule update --init tools/quickbook +rsync -av $TRAVIS_BUILD_DIR/ libs/$SELF +python tools/boostdep/depinst/depinst.py ../tools/quickbook +./bootstrap.sh +./b2 headers + +echo '==================================> SCRIPT' + +echo "using doxygen ; using boostbook ; using saxonhe ;" > tools/build/src/user-config.jam +./b2 -j3 libs/$SELF/doc//boostrelease + +elif [ "$DRONE_JOB_BUILDTYPE" == "codecov" ]; then + +echo '==================================> INSTALL' + +common_install + +echo '==================================> SCRIPT' + +cd $BOOST_ROOT/libs/$SELF +ci/travis/codecov.sh + +elif [ "$DRONE_JOB_BUILDTYPE" == "valgrind" ]; then + +echo '==================================> INSTALL' + +common_install + +echo '==================================> SCRIPT' + +cd $BOOST_ROOT/libs/$SELF +ci/travis/valgrind.sh + +elif [ "$DRONE_JOB_BUILDTYPE" == "standalone" ]; then + +echo '==================================> INSTALL' + +# Installing cmake with apt-get, so not required here: +# pip install --user cmake + +echo '==================================> SCRIPT' + +export CXXFLAGS="-Wall -Wextra -Werror -std=c++17" +mkdir __build_17 +cd __build_17 +cmake -DBOOST_JSON_STANDALONE=1 .. +cmake --build . +ctest -V . +export CXXFLAGS="-Wall -Wextra -Werror -std=c++2a" +mkdir ../__build_2a +cd ../__build_2a +cmake -DBOOST_JSON_STANDALONE=1 .. +cmake --build . +ctest -V . + +elif [ "$DRONE_JOB_BUILDTYPE" == "coverity" ]; then + +echo '==================================> INSTALL' + +common_install + +echo '==================================> SCRIPT' + +if [ $VARIANT = "process_valgrind" ]; +then export USE_VALGRIND="testing.launcher=valgrind valgrind=on"; +fi ; + +if [ -n "${COVERITY_SCAN_NOTIFICATION_EMAIL}" -a \( "$TRAVIS_BRANCH" = "develop" -o "$TRAVIS_BRANCH" = "master" \) -a \( "$DRONE_BUILD_EVENT" = "push" -o "$DRONE_BUILD_EVENT" = "cron" \) ] ; then +cd $BOOST_ROOT/libs/$SELF +ci/travis/coverity.sh +fi + +elif [ "$DRONE_JOB_BUILDTYPE" == "cmake-superproject" ]; then + +echo '==================================> INSTALL' + +common_install + +echo '==================================> COMPILE' + +export CXXFLAGS="-Wall -Wextra -Werror" + +mkdir __build_static +cd __build_static +cmake -DBOOST_ENABLE_CMAKE=1 -DBUILD_TESTING=ON -DBoost_VERBOSE=1 \ + -DBOOST_INCLUDE_LIBRARIES=$SELF .. +cmake --build . +ctest --output-on-failure -R boost_$SELF + +cd .. + +mkdir __build_shared +cd __build_shared +cmake -DBOOST_ENABLE_CMAKE=1 -DBUILD_TESTING=ON -DBoost_VERBOSE=1 \ + -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=ON .. +cmake --build . +ctest --output-on-failure -R boost_$SELF + +elif [ "$DRONE_JOB_BUILDTYPE" == "cmake1" ]; then + +echo '==================================> INSTALL' + +pip install --user cmake + +echo '==================================> SCRIPT' + +export SELF=`basename $REPO_NAME` +BOOST_BRANCH=develop && [ "$DRONE_BRANCH" == "master" ] && BOOST_BRANCH=master || true +echo BOOST_BRANCH: $BOOST_BRANCH +cd .. +git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root +cd boost-root +mkdir -p libs/$SELF +cp -r $DRONE_BUILD_DIR/* libs/$SELF +# git submodule update --init tools/boostdep +git submodule update --init --recursive + +cd libs/$SELF + +../../../b2 -sBOOST_BUILD_PATH=. +../../../b2 $MULTITHREAD with-valgrind address-model=64 architecture=x86 $USE_VALGRIND toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI +../../../b2 $MULTITHREAD without-valgrind address-model=64 architecture=x86 toolset=$TOOLSET cxxflags="--coverage -DBOOST_TRAVISCI_BUILD -std=$CXX_STANDARD" linkflags="--coverage" -sBOOST_BUILD_PATH=. $REPORT_CI + + + +fi \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 94f9b9c87..20e7499fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,12 +6,13 @@ cmake_minimum_required(VERSION 3.5...3.16) project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) +find_package(Boost REQUIRED iostreams program_options filesystem system thread) add_library(boost_process INTERFACE) add_library(Boost::process ALIAS boost_process) target_include_directories(boost_process INTERFACE include) - +include_directories(include) target_link_libraries(boost_process INTERFACE Boost::algorithm diff --git a/doc/tutorial.qbk b/doc/tutorial.qbk index 4365f0f54..6c88f9398 100644 --- a/doc/tutorial.qbk +++ b/doc/tutorial.qbk @@ -82,10 +82,10 @@ int result = bp::system("/usr/bin/g++", "main.cpp"); ``` With that syntax we still have "g++" hard-coded, so let's assume we get the string -from an external source as `boost::filesystem::path`, we can do this too. +from an external source as `boost::process::filesystem::path`, we can do this too. ``` -boost::filesystem::path p = "/usr/bin/g++"; //or get it from somewhere else. +boost::process::filesystem::path p = "/usr/bin/g++"; //or get it from somewhere else. int result = bp::system(p, "main.cpp"); ``` @@ -93,7 +93,7 @@ Now we might want to find the `g++` executable in the `PATH`-variable, as the `c `Boost.process` provides a function to this end: bp::search_path. ``` -boost::filesystem::path p = bp::search_path("g++"); //or get it from somewhere else. +boost::process::filesystem::path p = bp::search_path("g++"); //or get it from somewhere else. int result = bp::system(p, "main.cpp"); ``` diff --git a/example/io.cpp b/example/io.cpp index 1b1e8eb77..97ace20ad 100644 --- a/example/io.cpp +++ b/example/io.cpp @@ -22,7 +22,7 @@ int main() bp::std_in < bp::null //null in ); - boost::filesystem::path p = "input.txt"; + boost::process::filesystem::path p = "input.txt"; bp::system( "test.exe", diff --git a/example/start_dir.cpp b/example/start_dir.cpp index 896ef8cc8..fbeb19d52 100644 --- a/example/start_dir.cpp +++ b/example/start_dir.cpp @@ -8,7 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include -#include +#include namespace bp = boost::process; @@ -19,9 +19,9 @@ int main() bp::start_dir="../foo" ); - boost::filesystem::path exe = "test.exe"; + boost::process::filesystem::path exe = "test.exe"; bp::system( - boost::filesystem::absolute(exe), + boost::process::filesystem::absolute(exe), bp::start_dir="../foo" ); } diff --git a/example/windows.cpp b/example/windows.cpp index 98a838c8a..aba35853f 100644 --- a/example/windows.cpp +++ b/example/windows.cpp @@ -8,6 +8,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include #include @@ -22,9 +23,9 @@ int main() bp::system("test.exe", - bp::on_setup([](auto &e) + bp::extend::on_setup([](auto &e) { e.startup_info.dwFlags = STARTF_RUNFULLSCREEN; }), - bp::on_error([](auto&, const std::error_code & ec) + bp::extend::on_error([](auto&, const std::error_code & ec) { std::cerr << ec.message() << std::endl; }) ); } diff --git a/include/boost/process/detail/basic_cmd.hpp b/include/boost/process/detail/basic_cmd.hpp index 3da51ffe1..f3405bbb1 100644 --- a/include/boost/process/detail/basic_cmd.hpp +++ b/include/boost/process/detail/basic_cmd.hpp @@ -168,7 +168,7 @@ struct exe_builder string_type exe; std::vector args; - void operator()(const boost::filesystem::path & data) + void operator()(const boost::process::filesystem::path & data) { not_cmd = true; if (exe.empty()) diff --git a/include/boost/process/detail/posix/basic_pipe.hpp b/include/boost/process/detail/posix/basic_pipe.hpp index f9d0a4545..965db6370 100644 --- a/include/boost/process/detail/posix/basic_pipe.hpp +++ b/include/boost/process/detail/posix/basic_pipe.hpp @@ -11,7 +11,7 @@ #define BOOST_PROCESS_POSIX_PIPE_HPP -#include +#include #include #include #include @@ -95,7 +95,7 @@ class basic_pipe int_type read(char_type * data, int_type count) { int_type read_len; - while ((read_len = ::read(_source, data, count * sizeof(char_type))) == -1) + while ((read_len = static_cast(::read(_source, data, count * sizeof(char_type)))) == -1) { //Try again if interrupted auto err = errno; diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index fb2efedef..930e0c546 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -26,6 +26,8 @@ #include #include +#include + namespace boost { namespace process { namespace detail { namespace posix { template @@ -155,8 +157,8 @@ class executor const auto len = std::strlen(msg); int data[2] = {ec.value(), static_cast(len + 1)}; - ::write(_pipe_sink, &data[0], sizeof(int) * 2); - ::write(_pipe_sink, msg, len); + boost::ignore_unused(::write(_pipe_sink, &data[0], sizeof(int) * 2)); + boost::ignore_unused(::write(_pipe_sink, msg, len)); } void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::false_) diff --git a/include/boost/process/detail/posix/file_descriptor.hpp b/include/boost/process/detail/posix/file_descriptor.hpp index 4b481737b..1d897b11d 100644 --- a/include/boost/process/detail/posix/file_descriptor.hpp +++ b/include/boost/process/detail/posix/file_descriptor.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include namespace boost { namespace process { namespace detail { namespace posix { @@ -24,7 +24,7 @@ struct file_descriptor file_descriptor() = default; - explicit file_descriptor(const boost::filesystem::path& p, mode_t mode = read_write) + explicit file_descriptor(const boost::process::filesystem::path& p, mode_t mode = read_write) : file_descriptor(p.native(), mode) { } diff --git a/include/boost/process/detail/posix/search_path.hpp b/include/boost/process/detail/posix/search_path.hpp index ad781e57d..e06f4163e 100644 --- a/include/boost/process/detail/posix/search_path.hpp +++ b/include/boost/process/detail/posix/search_path.hpp @@ -11,7 +11,7 @@ #define BOOST_PROCESS_POSIX_SEARCH_PATH_HPP #include -#include +#include #include #include #include @@ -20,15 +20,15 @@ namespace boost { namespace process { namespace detail { namespace posix { -inline boost::filesystem::path search_path( - const boost::filesystem::path &filename, - const std::vector &path) +inline boost::process::filesystem::path search_path( + const boost::process::filesystem::path &filename, + const std::vector &path) { - for (const boost::filesystem::path & pp : path) + for (const boost::process::filesystem::path & pp : path) { auto p = pp / filename; boost::system::error_code ec; - bool file = boost::filesystem::is_regular_file(p, ec); + bool file = boost::process::filesystem::is_regular_file(p, ec); if (!ec && file && ::access(p.c_str(), X_OK) == 0) return p; } diff --git a/include/boost/process/detail/posix/shell_path.hpp b/include/boost/process/detail/posix/shell_path.hpp index 870cab6b9..c05d8ad92 100644 --- a/include/boost/process/detail/posix/shell_path.hpp +++ b/include/boost/process/detail/posix/shell_path.hpp @@ -12,16 +12,16 @@ #include #include -#include +#include namespace boost { namespace process { namespace detail { namespace posix { -inline boost::filesystem::path shell_path() +inline boost::process::filesystem::path shell_path() { return "/bin/sh"; } -inline boost::filesystem::path shell_path(std::error_code &ec) +inline boost::process::filesystem::path shell_path(std::error_code &ec) { ec.clear(); return "/bin/sh"; diff --git a/include/boost/process/detail/posix/start_dir.hpp b/include/boost/process/detail/posix/start_dir.hpp index ee4cb6bfb..5483bb73d 100644 --- a/include/boost/process/detail/posix/start_dir.hpp +++ b/include/boost/process/detail/posix/start_dir.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace boost { namespace process { namespace detail { namespace posix { @@ -26,7 +27,7 @@ struct start_dir_init : handler_base_ext template void on_exec_setup(PosixExecutor&) const { - ::chdir(s_.c_str()); + boost::ignore_unused(::chdir(s_.c_str())); } const string_type & str() const {return s_;} private: diff --git a/include/boost/process/detail/traits/cmd_or_exe.hpp b/include/boost/process/detail/traits/cmd_or_exe.hpp index 1c627122d..66af5b32d 100644 --- a/include/boost/process/detail/traits/cmd_or_exe.hpp +++ b/include/boost/process/detail/traits/cmd_or_exe.hpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include namespace boost { namespace process { namespace detail { @@ -53,12 +53,12 @@ template<> struct initializer_tag> { type template<> struct initializer_tag { - typedef cmd_or_exe_tag type; + typedef cmd_or_exe_tag type; }; -template<> struct initializer_tag +template<> struct initializer_tag { - typedef cmd_or_exe_tag type; + typedef cmd_or_exe_tag type; }; template diff --git a/include/boost/process/detail/traits/wchar_t.hpp b/include/boost/process/detail/traits/wchar_t.hpp index f509dd06d..6e10cb14f 100644 --- a/include/boost/process/detail/traits/wchar_t.hpp +++ b/include/boost/process/detail/traits/wchar_t.hpp @@ -20,7 +20,7 @@ namespace boost { namespace process { namespace detail { template struct is_wchar_t : std::false_type {}; -template<> struct is_wchar_t : std::is_same +template<> struct is_wchar_t : std::is_same { }; diff --git a/include/boost/process/detail/windows/environment.hpp b/include/boost/process/detail/windows/environment.hpp index 13aa587da..53408ac7a 100644 --- a/include/boost/process/detail/windows/environment.hpp +++ b/include/boost/process/detail/windows/environment.hpp @@ -232,6 +232,8 @@ basic_environment_impl::basic_environment_impl(const native_environment_im template inline auto basic_environment_impl::get(const string_type &id) -> string_type { + if (id.size() >= _data.size()) //ok, so it's impossible id is in there. + return string_type(_data.data()); if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == equal_sign())) return string_type(_data.data()); //null-char is handled by the string. diff --git a/include/boost/process/detail/windows/file_descriptor.hpp b/include/boost/process/detail/windows/file_descriptor.hpp index 187f6ebab..033df0288 100644 --- a/include/boost/process/detail/windows/file_descriptor.hpp +++ b/include/boost/process/detail/windows/file_descriptor.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include namespace boost { namespace process { namespace detail { namespace windows { @@ -40,7 +40,7 @@ struct file_descriptor } file_descriptor() = default; - file_descriptor(const boost::filesystem::path& p, mode_t mode = read_write) + file_descriptor(const boost::process::filesystem::path& p, mode_t mode = read_write) : file_descriptor(p.native(), mode) { } diff --git a/include/boost/process/detail/windows/handles.hpp b/include/boost/process/detail/windows/handles.hpp index 7a93ac254..bf06ce76a 100644 --- a/include/boost/process/detail/windows/handles.hpp +++ b/include/boost/process/detail/windows/handles.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace boost { namespace process { namespace detail { diff --git a/include/boost/process/detail/windows/search_path.hpp b/include/boost/process/detail/windows/search_path.hpp index fe267bdf7..681722128 100644 --- a/include/boost/process/detail/windows/search_path.hpp +++ b/include/boost/process/detail/windows/search_path.hpp @@ -11,8 +11,7 @@ #define BOOST_PROCESS_WINDOWS_SEARCH_PATH_HPP #include -#include -#include +#include #include #include #include @@ -24,9 +23,9 @@ namespace boost { namespace process { namespace detail { namespace windows { -inline boost::filesystem::path search_path( - const boost::filesystem::path &filename, - const std::vector &path) +inline boost::process::filesystem::path search_path( + const boost::process::filesystem::path &filename, + const std::vector &path) { const ::boost::process::wnative_environment ne{}; typedef typename ::boost::process::wnative_environment::const_entry_type value_type; @@ -55,15 +54,15 @@ inline boost::filesystem::path search_path( for (auto & ext : extensions) boost::to_lower(ext); - for (const boost::filesystem::path & pp_ : path) + for (const boost::process::filesystem::path & pp_ : path) { auto p = pp_ / filename; - for (boost::filesystem::path ext : extensions) + for (boost::process::filesystem::path ext : extensions) { - boost::filesystem::path pp_ext = p; + boost::process::filesystem::path pp_ext = p; pp_ext += ext; boost::system::error_code ec; - bool file = boost::filesystem::is_regular_file(pp_ext, ec); + bool file = boost::process::filesystem::is_regular_file(pp_ext, ec); if (!ec && file && ::boost::winapi::sh_get_file_info(pp_ext.native().c_str(), 0, 0, 0, ::boost::winapi::SHGFI_EXETYPE_)) { diff --git a/include/boost/process/detail/windows/shell_path.hpp b/include/boost/process/detail/windows/shell_path.hpp index 263a41054..bb150c944 100644 --- a/include/boost/process/detail/windows/shell_path.hpp +++ b/include/boost/process/detail/windows/shell_path.hpp @@ -12,29 +12,29 @@ #include #include -#include +#include #include #include namespace boost { namespace process { namespace detail { namespace windows { -inline boost::filesystem::path shell_path() +inline boost::process::filesystem::path shell_path() { ::boost::winapi::WCHAR_ sysdir[260]; unsigned int size = ::boost::winapi::get_system_directory(sysdir, sizeof(sysdir)); if (!size) throw_last_error("GetSystemDirectory() failed"); - boost::filesystem::path p = sysdir; + boost::process::filesystem::path p = sysdir; return p / "cmd.exe"; } -inline boost::filesystem::path shell_path(std::error_code &ec) noexcept +inline boost::process::filesystem::path shell_path(std::error_code &ec) noexcept { ::boost::winapi::WCHAR_ sysdir[260]; unsigned int size = ::boost::winapi::get_system_directory(sysdir, sizeof(sysdir)); - boost::filesystem::path p; + boost::process::filesystem::path p; if (!size) ec = std::error_code( ::boost::winapi::GetLastError(), diff --git a/include/boost/process/environment.hpp b/include/boost/process/environment.hpp index 205e1130a..3aa4c9160 100644 --- a/include/boost/process/environment.hpp +++ b/include/boost/process/environment.hpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #if defined(BOOST_POSIX_API) #include @@ -263,7 +263,9 @@ class basic_environment_impl : public Implementation auto st1 = key + ::boost::process::detail::equal_sign(); while (*p != nullptr) { - if (std::equal(st1.begin(), st1.end(), *p)) + const int len = std::char_traits::length(*p); + if ((std::distance(st1.begin(), st1.end()) < len) + && std::equal(st1.begin(), st1.end(), *p)) break; p++; } @@ -275,7 +277,9 @@ class basic_environment_impl : public Implementation auto st1 = key + ::boost::process::detail::equal_sign(); while (*p != nullptr) { - if (std::equal(st1.begin(), st1.end(), *p)) + const int len = std::char_traits::length(*p); + if ((std::distance(st1.begin(), st1.end()) < len) + && std::equal(st1.begin(), st1.end(), *p)) break; p++; } @@ -288,7 +292,9 @@ class basic_environment_impl : public Implementation auto st1 = st + ::boost::process::detail::equal_sign(); while (*p != nullptr) { - if (std::equal(st1.begin(), st1.end(), *p)) + const int len = std::char_traits::length(*p); + if ((std::distance(st1.begin(), st1.end()) < len) + && std::equal(st1.begin(), st1.end(), *p)) return 1u; p++; } @@ -672,7 +678,7 @@ inline native_environment environment() { return ::boost::process:: native_env ///Get the enviroment of the current process. inline wnative_environment wenvironment() { return ::boost::process::wnative_environment(); } ///Get the path environment variable of the current process runs. -inline std::vector path() +inline std::vector path() { #if defined(BOOST_WINDOWS_API) const ::boost::process::wnative_environment ne{}; @@ -693,7 +699,7 @@ inline std::vector path() auto vec = itr->to_vector(); - std::vector val; + std::vector val; val.resize(vec.size()); std::copy(vec.begin(), vec.end(), val.begin()); diff --git a/include/boost/process/exe.hpp b/include/boost/process/exe.hpp index 8949984c0..c9358c0a7 100644 --- a/include/boost/process/exe.hpp +++ b/include/boost/process/exe.hpp @@ -36,15 +36,15 @@ namespace detail { struct exe_ { template - inline exe_setter_ operator()(const boost::filesystem::path & pth) const + inline exe_setter_ operator()(const boost::process::filesystem::path & pth) const { - return exe_setter_(pth.native()); + return exe_setter_(pth.native()); } template - inline exe_setter_ operator=(const boost::filesystem::path & pth) const + inline exe_setter_ operator=(const boost::process::filesystem::path & pth) const { - return exe_setter_(pth.native()); + return exe_setter_(pth.native()); } @@ -79,7 +79,7 @@ The overload form applies when to the first, when several strings are passed to function. The following expressions are valid, with `value` being either a C-String or -a `std::basic_string` with `char` or `wchar_t` or a `boost::filesystem::path`. +a `std::basic_string` with `char` or `wchar_t` or a `boost::process::filesystem::path`. \code{.cpp} exe="value"; diff --git a/include/boost/process/filesystem.hpp b/include/boost/process/filesystem.hpp new file mode 100644 index 000000000..4e1c12e84 --- /dev/null +++ b/include/boost/process/filesystem.hpp @@ -0,0 +1,28 @@ +// Copyright (c) 2021 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_FILESYSTEM_HPP +#define BOOST_PROCESS_FILESYSTEM_HPP + +#ifdef BOOST_PROCESS_USE_STD_FS +#include +#else +#include +#include +#endif + +namespace boost +{ +namespace process +{ +#ifdef BOOST_PROCESS_USE_STD_FS +namespace filesystem = std::filesystem; +#else +namespace filesystem = boost::filesystem; +#endif + +} +} + +#endif //BOOST_PROCESS_FILESYSTEM_HPP diff --git a/include/boost/process/io.hpp b/include/boost/process/io.hpp index c929e799f..8238de686 100644 --- a/include/boost/process/io.hpp +++ b/include/boost/process/io.hpp @@ -60,9 +60,9 @@ namespace boost { The library allows full redirection of streams to files as shown below. \code{.cpp} -boost::filesystem::path log = "my_log_file.txt"; -boost::filesystem::path input = "input.txt"; -boost::filesystem::path output = "output.txt"; +boost::process::filesystem::path log = "my_log_file.txt"; +boost::process::filesystem::path input = "input.txt"; +boost::process::filesystem::path output = "output.txt"; system("my_prog", std_out>output, std_inlog); \endcode @@ -152,13 +152,13 @@ struct std_in_ api::null_in operator=(const null_t &) const {return api::null_in();} api::null_in operator<(const null_t &) const {return api::null_in();} - api::file_in operator=(const boost::filesystem::path &p) const {return p;} + api::file_in operator=(const boost::process::filesystem::path &p) const {return p;} api::file_in operator=(const std::string & p) const {return p;} api::file_in operator=(const std::wstring &p) const {return p;} api::file_in operator=(const char * p) const {return p;} api::file_in operator=(const wchar_t * p) const {return p;} - api::file_in operator<(const boost::filesystem::path &p) const {return p;} + api::file_in operator<(const boost::process::filesystem::path &p) const {return p;} api::file_in operator<(const std::string &p) const {return p;} api::file_in operator<(const std::wstring &p) const {return p;} api::file_in operator<(const char*p) const {return p;} @@ -209,13 +209,13 @@ struct std_out_ api::null_out operator=(const null_t &) const {return api::null_out();} api::null_out operator>(const null_t &) const {return api::null_out();} - api::file_out operator=(const boost::filesystem::path &p) const {return api::file_out(p);} + api::file_out operator=(const boost::process::filesystem::path &p) const {return api::file_out(p);} api::file_out operator=(const std::string &p) const {return api::file_out(p);} api::file_out operator=(const std::wstring &p) const {return api::file_out(p);} api::file_out operator=(const char * p) const {return api::file_out(p);} api::file_out operator=(const wchar_t * p) const {return api::file_out(p);} - api::file_out operator>(const boost::filesystem::path &p) const {return api::file_out(p);} + api::file_out operator>(const boost::process::filesystem::path &p) const {return api::file_out(p);} api::file_out operator>(const std::string &p) const {return api::file_out(p);} api::file_out operator>(const std::wstring &p) const {return api::file_out(p);} api::file_out operator>(const char * p) const {return api::file_out(p);} @@ -282,7 +282,7 @@ This property allows to set the input stream for the child process. The file I/O simple redirects the stream to a file, for which the possible types are - - `boost::filesystem::path` + - `boost::process::filesystem::path` - `std::basic_string` - `const char_type*` - `FILE*` @@ -424,7 +424,7 @@ This property allows to set the output stream for the child process. The file I/O simple redirects the stream to a file, for which the possible types are - - `boost::filesystem::path` + - `boost::process::filesystem::path` - `std::basic_string` - `const char_type*` - `FILE*` diff --git a/include/boost/process/search_path.hpp b/include/boost/process/search_path.hpp index ee1f60525..7e33ec552 100644 --- a/include/boost/process/search_path.hpp +++ b/include/boost/process/search_path.hpp @@ -44,8 +44,8 @@ namespace boost { namespace process { * \returns the absolute path to the executable filename or an * empty string if filename isn't found */ -inline boost::filesystem::path search_path(const boost::filesystem::path &filename, - const std::vector path = ::boost::this_process::path()) +inline boost::process::filesystem::path search_path(const boost::process::filesystem::path &filename, + const std::vector path = ::boost::this_process::path()) { return ::boost::process::detail::api::search_path(filename, path); } diff --git a/include/boost/process/shell.hpp b/include/boost/process/shell.hpp index 64314a688..87313d53f 100644 --- a/include/boost/process/shell.hpp +++ b/include/boost/process/shell.hpp @@ -44,18 +44,18 @@ struct shell_ { constexpr shell_() {} - boost::filesystem::path operator()() const + boost::process::filesystem::path operator()() const { return boost::process::detail::api::shell_path(); } - boost::filesystem::path operator()(std::error_code & ec) const noexcept + boost::process::filesystem::path operator()(std::error_code & ec) const noexcept { return boost::process::detail::api::shell_path(ec); } }; template<> -struct is_wchar_t : is_wchar_t +struct is_wchar_t : is_wchar_t { }; diff --git a/include/boost/process/start_dir.hpp b/include/boost/process/start_dir.hpp index 406e78e41..49f4354a8 100644 --- a/include/boost/process/start_dir.hpp +++ b/include/boost/process/start_dir.hpp @@ -23,7 +23,7 @@ #include #include -#include +#include /** \file boost/process/start_dir.hpp * @@ -53,8 +53,8 @@ struct start_dir_ api::start_dir_init operator()(std::basic_string && s) const {return {std::move(s)}; } template api::start_dir_init operator()(const Char* s) const {return {s}; } - api::start_dir_init - operator()(const boost::filesystem::path & st) const {return {st.native()}; } + api::start_dir_init + operator()(const boost::process::filesystem::path & st) const {return {st.native()}; } template api::start_dir_init operator= (const std::basic_string & st) const {return {st}; } @@ -62,8 +62,8 @@ struct start_dir_ api::start_dir_init operator= (std::basic_string && s) const {return {std::move(s)}; } template api::start_dir_init operator= (const Char* s) const {return {s}; } - api::start_dir_init - operator= (const boost::filesystem::path & st) const {return {st.native()}; } + api::start_dir_init + operator= (const boost::process::filesystem::path & st) const {return {st.native()}; } }; @@ -100,7 +100,7 @@ start_dir=path start_dir(path) \endcode -It can be used with `std::string`, `std::wstring` and `boost::filesystem::path`. +It can be used with `std::string`, `std::wstring` and `boost::process::filesystem::path`. */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 000000000..eaf671da1 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,74 @@ +enable_testing() + + +add_executable(sparring_partner sparring_partner.cpp ) +target_link_libraries(sparring_partner Boost::program_options Boost::filesystem Boost::iostreams) + +add_executable(exit_argc exit_argc.cpp) + +add_executable(sub_launch sub_launcher.cpp) +target_link_libraries(sparring_partner Boost::program_options Boost::filesystem Boost::iostreams Boost::system) + +function(process_standalone_test name ) + add_executable(${name} ${name}.cpp) + target_link_libraries(${name} Boost::system Boost::filesystem) + add_test(NAME ${name} COMMAND $ ) +endfunction() + +process_standalone_test(environment) +process_standalone_test(async_pipe) +process_standalone_test(pipe) + +function(process_sub_launch_test name ) + add_executable(${name} ${name}.cpp) + target_link_libraries(${name} Boost::system Boost::filesystem Boost::thread) + add_test(NAME ${name} COMMAND $ $ ) +endfunction() + +process_sub_launch_test(group) +process_sub_launch_test(group_wait) + +function(process_sparring_partner_launch name ) + add_executable(${name} ${name}.cpp) + target_link_libraries(${name} Boost::system Boost::filesystem Boost::thread) + add_test(NAME ${name} COMMAND $ $ ) +endfunction() + +process_sparring_partner_launch(async) +process_sparring_partner_launch(async_fut) +process_sparring_partner_launch(args_handling) +process_sparring_partner_launch(args_cmd) +process_sparring_partner_launch(wargs_cmd) +process_sparring_partner_launch(bind_stderr) +process_sparring_partner_launch(bind_stdin) +process_sparring_partner_launch(bind_stdin_stdout) +process_sparring_partner_launch(bind_stdout) +process_sparring_partner_launch(bind_stdout_stderr) +process_sparring_partner_launch(pipe_fwd) +process_sparring_partner_launch(cmd_test) +process_sparring_partner_launch(close_stderr) +process_sparring_partner_launch(close_stdin) +process_sparring_partner_launch(close_stdout) +process_sparring_partner_launch(error) +process_sparring_partner_launch(exit_code) +process_sparring_partner_launch(extensions) +process_sparring_partner_launch(env) +process_sparring_partner_launch(limit_fd) +process_sparring_partner_launch(run_exe) +process_sparring_partner_launch(run_exe_path) +process_sparring_partner_launch(search_path) +process_sparring_partner_launch(shell) +process_sparring_partner_launch(shell_path) +process_sparring_partner_launch(system_test1) +process_sparring_partner_launch(system_test2) +process_sparring_partner_launch(spawn) +process_sparring_partner_launch(start_dir) +process_sparring_partner_launch(terminate) +process_sparring_partner_launch(throw_on_error) +process_sparring_partner_launch(wait) +process_sparring_partner_launch(wait_for) +process_sparring_partner_launch(on_exit) +process_sparring_partner_launch(on_exit2) +process_sparring_partner_launch(on_exit3) +process_sparring_partner_launch(posix_specific) +process_sparring_partner_launch(windows_specific) \ No newline at end of file diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 329634cc0..2563d9052 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -70,7 +70,7 @@ rule test-options ( name ) test-suite bare : [ run environment.cpp system filesystem : [ test-options environment ] ] - [ run async_pipe.cpp system filesystem : [ test-options async_pipe ] ] + [ run async_pipe.cpp system filesystem : [ test-options async_pipe ] : : darwin:no ] [ run pipe.cpp system filesystem : [ test-options pipe ] ] [ compile no_ansi_apps.cpp ] [ compile-fail spawn_fail.cpp ] @@ -100,12 +100,12 @@ test-suite with-valgrind : [ run env.cpp program_options system filesystem : [ test-options env ] : sparring_partner ] [ run group.cpp system thread filesystem : [ test-options group ] : sub_launch ] [ run group.cpp system thread filesystem : [ test-options group ] : sub_launch : no windows:yes BOOST_USE_WINDOWS_H=1 : group-windows-h ] - [ run group_wait.cpp system thread filesystem : [ test-options group_wait ] : sparring_partner ] + [ run group_wait.cpp system thread filesystem : [ test-options group_wait ] : sparring_partner : darwin:no ] [ run limit_fd.cpp program_options system filesystem : [ test-options limit_fd ] : sparring_partner ] [ run run_exe.cpp filesystem : : sparring_partner ] [ run run_exe_path.cpp filesystem : [ test-options run_exe_path ] : sparring_partner ] [ run search_path.cpp filesystem system : [ test-options search_path ] : : windows:shell32 ] - [ run shell.cpp filesystem system : [ test-options shell ] : sparring_partner ] + [ run shell.cpp filesystem system : [ test-options shell ] : sparring_partner : darwin:no ] [ run shell_path.cpp filesystem system : [ test-options shell_path ] ] [ run system_test1.cpp filesystem system : [ test-options system_test1 ] : sparring_partner ] [ run system_test2.cpp filesystem system : [ test-options system_test2 ] : sparring_partner ] @@ -125,7 +125,7 @@ test-suite with-valgrind : test-suite without-valgrind : [ run async_system_future.cpp filesystem system coroutine : [ test-options async_system_future ] : sparring_partner : static msvc:/bigobj ] [ run async_system_stackful.cpp filesystem system coroutine : [ test-options async_system_stackful ] : sparring_partner : static msvc:/bigobj ] - [ run async_system_stackful_error.cpp filesystem system coroutine : [ test-options async_system_stackful_error ] : sparring_partner : static msvc:/bigobj ] +# [ run async_system_stackful_error.cpp filesystem system coroutine : [ test-options async_system_stackful_error ] : sparring_partner : static msvc:/bigobj ] [ run async_system_stackful_except.cpp filesystem system coroutine : [ test-options async_system_stackful_except ] : sparring_partner : static msvc:/bigobj ] [ run async_system_stackless.cpp filesystem system coroutine : [ test-options async_system_stackless ] : sparring_partner : static msvc:/bigobj ] [ run vfork.cpp system filesystem : [ test-options vfork ] : sparring_partner : no linux:yes ] diff --git a/test/args_handling.cpp b/test/args_handling.cpp index 699659367..4377a836c 100644 --- a/test/args_handling.cpp +++ b/test/args_handling.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(implicit_args_fs_path) { using boost::unit_test::framework::master_test_suite; - boost::filesystem::path exe = master_test_suite().argv[1]; + boost::process::filesystem::path exe = master_test_suite().argv[1]; std::error_code ec; bp::child c( @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(explicit_args_fs_path) { using boost::unit_test::framework::master_test_suite; - boost::filesystem::path exe = master_test_suite().argv[1]; + boost::process::filesystem::path exe = master_test_suite().argv[1]; std::error_code ec; bp::child c( diff --git a/test/async.cpp b/test/async.cpp index e12f4bf53..4b5957eee 100644 --- a/test/async.cpp +++ b/test/async.cpp @@ -147,8 +147,8 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(1 timeout2.async_wait([&](boost::system::error_code ec){if (!ec) io_context2.stop();}); std::error_code ec; - bool exit_called_for_c1 = false; - int exit_code_c1 = 0; + std::atomic exit_called_for_c1 {false}; + std::atomic exit_code_c1 {0}; bp::child c1( master_test_suite().argv[1], "test", "--exit-code", "1", @@ -164,8 +164,8 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(1 ); BOOST_REQUIRE(!ec); - bool exit_called_for_c2 = false; - int exit_code_c2 = 0; + std::atomic exit_called_for_c2 {false}; + std::atomic exit_code_c2{0}; bp::child c2( master_test_suite().argv[1], "test", "--exit-code", "2", "--wait", "4", @@ -174,7 +174,8 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(1 bp::on_exit([&](int exit, const std::error_code& ec_in) { BOOST_CHECK(!exit_called_for_c2); - exit_code_c2 = exit; exit_called_for_c2=true; + exit_code_c2 = exit; + exit_called_for_c2=true; BOOST_CHECK(!ec_in); timeout2.cancel(); }) diff --git a/test/async_pipe.cpp b/test/async_pipe.cpp index ef6cab4be..0507dbd7a 100644 --- a/test/async_pipe.cpp +++ b/test/async_pipe.cpp @@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE(move_pipe) ap_inv.close(); const auto ap3 = std::move(ap_inv); } - +/* { //copy an a closed pipe BOOST_TEST_CHECKPOINT("Copy assign"); @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(move_pipe) BOOST_TEST_CHECKPOINT("Copy construct"); bp::async_pipe ap4{ap_inv}; } - +*/ } diff --git a/test/bind_stderr.cpp b/test/bind_stderr.cpp index 6f0c11d16..08762c005 100644 --- a/test/bind_stderr.cpp +++ b/test/bind_stderr.cpp @@ -22,7 +22,8 @@ #include #include -#include +#include +#include #include #include @@ -37,7 +38,7 @@ typedef boost::asio::windows::stream_handle pipe_end; typedef boost::asio::posix::stream_descriptor pipe_end; #endif -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; BOOST_AUTO_TEST_SUITE( bind_stderr ); @@ -148,7 +149,7 @@ BOOST_AUTO_TEST_CASE(file_io, *boost::unit_test::timeout(2)) is >> s; BOOST_CHECK_EQUAL(s, "hello"); } - boost::filesystem::remove(pth); + boost::process::filesystem::remove(pth); } diff --git a/test/bind_stdin.cpp b/test/bind_stdin.cpp index 6f03326e6..afd81d027 100644 --- a/test/bind_stdin.cpp +++ b/test/bind_stdin.cpp @@ -19,7 +19,8 @@ #include #include -#include +#include +#include #include #include @@ -41,7 +42,7 @@ typedef boost::asio::posix::stream_descriptor pipe_end; #endif -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(10)) @@ -177,7 +178,7 @@ BOOST_AUTO_TEST_CASE(file_io, *boost::unit_test::timeout(2)) bp::ipstream is; { - boost::filesystem::ofstream fs(pth); + boost::process::filesystem::ofstream fs(pth); fs << 321 << std::endl; fs << 1.2345 << std::endl; fs << "some_string" << std::endl; @@ -205,7 +206,7 @@ BOOST_AUTO_TEST_CASE(file_io, *boost::unit_test::timeout(2)) BOOST_CHECK_EQUAL(s, "abcsome_string"); c.wait(); - boost::filesystem::remove(pth); + boost::process::filesystem::remove(pth); } BOOST_AUTO_TEST_CASE(file_io_C, *boost::unit_test::timeout(2)) @@ -220,7 +221,7 @@ BOOST_AUTO_TEST_CASE(file_io_C, *boost::unit_test::timeout(2)) bp::ipstream is; { - boost::filesystem::ofstream fs(pth); + boost::process::filesystem::ofstream fs(pth); fs << 321 << std::endl; fs << 1.2345 << std::endl; fs << "some_string" << std::endl; @@ -254,7 +255,7 @@ BOOST_AUTO_TEST_CASE(file_io_C, *boost::unit_test::timeout(2)) BOOST_CHECK_EQUAL(s, "abcsome_string"); c.wait(); - boost::filesystem::remove(pth); + boost::process::filesystem::remove(pth); } BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file diff --git a/test/bind_stdin_stdout.cpp b/test/bind_stdin_stdout.cpp index 1f71e44bd..c2ee29280 100644 --- a/test/bind_stdin_stdout.cpp +++ b/test/bind_stdin_stdout.cpp @@ -10,6 +10,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_IGNORE_SIGCHLD #include +#include #include #include diff --git a/test/bind_stdout.cpp b/test/bind_stdout.cpp index 6798bea5d..da7fe7765 100644 --- a/test/bind_stdout.cpp +++ b/test/bind_stdout.cpp @@ -10,6 +10,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_IGNORE_SIGCHLD #include +#include #include @@ -23,7 +24,7 @@ #include #include -#include +#include #include #include @@ -40,7 +41,7 @@ typedef boost::asio::posix::stream_descriptor pipe_end; BOOST_AUTO_TEST_SUITE( bind_stdout ); -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; BOOST_AUTO_TEST_CASE(sync_io, *boost::unit_test::timeout(5)) @@ -164,7 +165,7 @@ BOOST_AUTO_TEST_CASE(file_io, *boost::unit_test::timeout(2)) is >> s; BOOST_CHECK_EQUAL(s, "hello"); } - boost::filesystem::remove(pth); + boost::process::filesystem::remove(pth); } diff --git a/test/bind_stdout_stderr.cpp b/test/bind_stdout_stderr.cpp index a10f0a59b..d108ba327 100644 --- a/test/bind_stdout_stderr.cpp +++ b/test/bind_stdout_stderr.cpp @@ -10,6 +10,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_IGNORE_SIGCHLD #include +#include #include #include diff --git a/test/cmd_test.cpp b/test/cmd_test.cpp index 98b84df6d..47ca034b9 100644 --- a/test/cmd_test.cpp +++ b/test/cmd_test.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include @@ -27,7 +27,7 @@ #include namespace bp = boost::process; -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; BOOST_AUTO_TEST_CASE(excplicit) diff --git a/test/group_wait.cpp b/test/group_wait.cpp index 5f6c55730..142645f68 100644 --- a/test/group_wait.cpp +++ b/test/group_wait.cpp @@ -7,6 +7,7 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + #define BOOST_TEST_MAIN #define BOOST_TEST_IGNORE_SIGCHLD #include @@ -31,13 +32,15 @@ namespace bp = boost::process; + + BOOST_AUTO_TEST_CASE(wait_group_test, *boost::unit_test::timeout(5)) { std::atomic done{false}; std::thread thr{ [&] { - for (int i = 0; i < 50 && !done.load(); i++) + for (int i = 0; i < 100 && !done.load(); i++) std::this_thread::sleep_for(std::chrono::milliseconds(100)); BOOST_REQUIRE(done.load()); }}; @@ -132,4 +135,7 @@ BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15)) done.store(true); thr.join(); -} \ No newline at end of file +} + + + diff --git a/test/limit_fd.cpp b/test/limit_fd.cpp index c2807e939..fa33ee807 100644 --- a/test/limit_fd.cpp +++ b/test/limit_fd.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include @@ -27,9 +27,12 @@ #if defined(BOOST_WINDOWS_API) #include #include + +#elif defined(__APPLE__) +#include #endif -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; namespace bt = boost::this_process; @@ -98,7 +101,8 @@ BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5)) int event_fd =::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); BOOST_CHECK(!bt::is_stream_handle(event_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); #endif - int dir_fd = ::dirfd(::opendir(".")); + auto od = ::opendir("."); + int dir_fd = ::dirfd(od); BOOST_CHECK(!bt::is_stream_handle(dir_fd , ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); #endif @@ -115,6 +119,9 @@ BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5)) BOOST_CHECK(bt::is_stream_handle(socket_to_handle(udp_socket.native_handle()), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK(bt::is_stream_handle(std::move(ap).sink(). native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); BOOST_CHECK(bt::is_stream_handle(std::move(ap).source().native_handle(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message()); +#if !defined( BOOST_WINDOWS_API ) + ::closedir(od); +#endif } struct on_setup_t diff --git a/test/pipe.cpp b/test/pipe.cpp index cb0caf8dc..750373916 100644 --- a/test/pipe.cpp +++ b/test/pipe.cpp @@ -39,8 +39,7 @@ BOOST_AUTO_TEST_CASE(named, *boost::unit_test::timeout(2)) #if defined( BOOST_WINDOWS_API ) bp::pipe pipe("\\\\.\\pipe\\pipe_name"); #elif defined( BOOST_POSIX_API ) - const auto home_path = boost::this_process::environment()["HOME"].to_string(); - bp::pipe pipe(home_path + "/.boost_process_test_pipe"); + bp::pipe pipe("./.boost_process_test_pipe"); #endif std::string in = "xyz"; diff --git a/test/posix_specific.cpp b/test/posix_specific.cpp index b37cc9294..86140f584 100644 --- a/test/posix_specific.cpp +++ b/test/posix_specific.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include @@ -23,7 +23,7 @@ #include #include -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; BOOST_AUTO_TEST_CASE(bind_fd, *boost::unit_test::timeout(2)) diff --git a/test/run_exe_path.cpp b/test/run_exe_path.cpp index 118fda7dc..67a9c5585 100644 --- a/test/run_exe_path.cpp +++ b/test/run_exe_path.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(run_exe_success) { using boost::unit_test::framework::master_test_suite; - boost::filesystem::path exe = master_test_suite().argv[1]; + boost::process::filesystem::path exe = master_test_suite().argv[1]; std::error_code ec; bp::child c( @@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(run_exe_success) #if defined(BOOST_WINDOWS_API) BOOST_AUTO_TEST_CASE(run_exe_error) { - boost::filesystem::path exe = "doesnt-exist"; + boost::process::filesystem::path exe = "doesnt-exist"; std::error_code ec; bp::child c( diff --git a/test/search_path.cpp b/test/search_path.cpp index 2dfc6fa75..31f0844f6 100644 --- a/test/search_path.cpp +++ b/test/search_path.cpp @@ -10,11 +10,11 @@ #define BOOST_TEST_MAIN #include #include -#include +#include #include namespace bp = boost::process; -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; BOOST_AUTO_TEST_CASE(search_path) { diff --git a/test/shell.cpp b/test/shell.cpp index ed4e48a3e..e0a97c958 100644 --- a/test/shell.cpp +++ b/test/shell.cpp @@ -7,6 +7,7 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + #define BOOST_TEST_MAIN #define BOOST_TEST_IGNORE_SIGCHLD #include @@ -23,6 +24,7 @@ namespace bp = boost::process; + BOOST_AUTO_TEST_CASE(shell_simple, *boost::unit_test::timeout(5)) { using boost::unit_test::framework::master_test_suite; @@ -48,6 +50,8 @@ BOOST_AUTO_TEST_CASE(shell_simple, *boost::unit_test::timeout(5)) BOOST_TEST_CHECKPOINT("Finished read"); BOOST_CHECK_EQUAL(s, "hello"); + c.wait(); + BOOST_CHECK_EQUAL(c.exit_code(), 0); } BOOST_AUTO_TEST_CASE(shell_error, *boost::unit_test::timeout(5)) @@ -61,3 +65,4 @@ BOOST_AUTO_TEST_CASE(shell_error, *boost::unit_test::timeout(5)) c2.wait(); BOOST_CHECK(c2.exit_code() != 0); } + diff --git a/test/shell_path.cpp b/test/shell_path.cpp index dce7cdeb2..9c2201c4a 100644 --- a/test/shell_path.cpp +++ b/test/shell_path.cpp @@ -10,7 +10,7 @@ #define BOOST_TEST_MAIN #include #include -#include +#include #include namespace bp = boost::process; @@ -18,13 +18,13 @@ namespace bp = boost::process; BOOST_AUTO_TEST_CASE(shell_set_on_error) { std::error_code ec; - boost::filesystem::path p = bp::shell(ec); + boost::process::filesystem::path p = bp::shell(ec); BOOST_CHECK(!ec); - BOOST_CHECK(boost::filesystem::exists(p)); + BOOST_CHECK(boost::process::filesystem::exists(p)); } BOOST_AUTO_TEST_CASE(shell_throw_on_error) { BOOST_CHECK_NO_THROW(bp::shell()); - BOOST_CHECK(boost::filesystem::exists(bp::shell())); + BOOST_CHECK(boost::process::filesystem::exists(bp::shell())); } diff --git a/test/sparring_partner.cpp b/test/sparring_partner.cpp index c628832a1..fe28c3cea 100644 --- a/test/sparring_partner.cpp +++ b/test/sparring_partner.cpp @@ -10,7 +10,7 @@ #define BOOST_USE_WINDOWS_H #include -#include +#include #include #include #include @@ -170,7 +170,7 @@ int main(int argc, char *argv[]) } else if (vm["pwd"].as()) { - std::cout << boost::filesystem::current_path().string() << std::endl; + std::cout << boost::process::filesystem::current_path().string() << std::endl; } else if (vm.count("query")) { diff --git a/test/spawn.cpp b/test/spawn.cpp index 25a2ecb3f..b823722aa 100644 --- a/test/spawn.cpp +++ b/test/spawn.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -38,7 +38,7 @@ typedef boost::asio::windows::stream_handle pipe_end; typedef boost::asio::posix::stream_descriptor pipe_end; #endif -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; BOOST_AUTO_TEST_CASE(sync_spawn, *boost::unit_test::timeout(5)) diff --git a/test/spawn_fail.cpp b/test/spawn_fail.cpp index 3fa963ef8..199773e4e 100644 --- a/test/spawn_fail.cpp +++ b/test/spawn_fail.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include @@ -34,7 +34,7 @@ typedef boost::asio::windows::stream_handle pipe_end; typedef boost::asio::posix::stream_descriptor pipe_end; #endif -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; int main() diff --git a/test/start_dir.cpp b/test/start_dir.cpp index 706c511c9..84c677aa6 100644 --- a/test/start_dir.cpp +++ b/test/start_dir.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -24,8 +24,8 @@ struct test_dir { std::string s_; test_dir(const std::string &s) : s_(s) - { BOOST_REQUIRE_NO_THROW(boost::filesystem::create_directory(s)); } - ~test_dir() { boost::filesystem::remove(s_); } + { BOOST_REQUIRE_NO_THROW(boost::process::filesystem::create_directory(s)); } + ~test_dir() { boost::process::filesystem::remove(s_); } }; BOOST_AUTO_TEST_CASE(start_in_dir) @@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(start_in_dir) std::error_code ec; bp::child c( - bp::exe=boost::filesystem::absolute(master_test_suite().argv[1]).string(), + bp::exe=boost::process::filesystem::absolute(master_test_suite().argv[1]).string(), bp::args +={"test", "--pwd"}, bp::start_dir = dir.s_, bp::std_out>is, @@ -49,8 +49,8 @@ BOOST_AUTO_TEST_CASE(start_in_dir) std::string s; std::getline(is, s); - auto path_read = boost::filesystem::absolute(boost::filesystem::path(s)).string(); - auto path_set = boost::filesystem::absolute(dir.s_).string(); + auto path_read = boost::process::filesystem::absolute(boost::process::filesystem::path(s)).string(); + auto path_set = boost::process::filesystem::absolute(dir.s_).string(); if (path_read.size() > path_set.size()) path_read.resize(path_set.size()); diff --git a/test/sub_launcher.cpp b/test/sub_launcher.cpp index c36490132..3579b3bf0 100644 --- a/test/sub_launcher.cpp +++ b/test/sub_launcher.cpp @@ -12,8 +12,8 @@ #include #include - #include +#include int main(int argc, char *argv[]) diff --git a/test/system_test1.cpp b/test/system_test1.cpp index 71aedd33f..3b45b9979 100644 --- a/test/system_test1.cpp +++ b/test/system_test1.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include @@ -43,7 +43,7 @@ typedef boost::asio::windows::stream_handle pipe_end; typedef boost::asio::posix::stream_descriptor pipe_end; #endif -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; BOOST_AUTO_TEST_CASE(system_exit_code, *boost::unit_test::timeout(5)) diff --git a/test/system_test2.cpp b/test/system_test2.cpp index e5d670233..037d1799f 100644 --- a/test/system_test2.cpp +++ b/test/system_test2.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include @@ -37,7 +37,7 @@ #include #include -namespace fs = boost::filesystem; +namespace fs = boost::process::filesystem; namespace bp = boost::process; BOOST_AUTO_TEST_CASE(explicit_async_io, *boost::unit_test::timeout(2)) diff --git a/test/wait_for.cpp b/test/wait_for.cpp index 9ee58e495..daef5eab7 100644 --- a/test/wait_for.cpp +++ b/test/wait_for.cpp @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(wait_until) auto now = std::chrono::system_clock::now(); auto t1 = now + std::chrono::milliseconds(400); - auto t2 = now + std::chrono::milliseconds(1200); + auto t2 = now + std::chrono::milliseconds(2000); BOOST_CHECK(!c.wait_until(t1)); BOOST_CHECK( c.wait_until(t2)); @@ -103,7 +103,7 @@ BOOST_AUTO_TEST_CASE(wait_until_ec) auto now = std::chrono::system_clock::now(); auto t1 = now + std::chrono::milliseconds(400); - auto t2 = now + std::chrono::milliseconds(1200); + auto t2 = now + std::chrono::milliseconds(2000); BOOST_CHECK(!c.wait_until(t1, ec)); BOOST_CHECK( c.wait_until(t2, ec)); From 397e6850536c51c8b2f67504a1b7f473d4ac10ec Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 19 May 2022 17:20:36 +0800 Subject: [PATCH 044/397] Updated readme. --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 69aec235a..cc1de7cc0 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,21 @@ -# [Boost Process (Boost.Process)](https://github.com/klemens-morgenstern/boost-process) +# [Boost Process (Boost.Process)](https://github.com/boostorg/process) Boost.process is a library for comfortable management of processes, released with boost 1.64.0. ### Test results -Branches | Linux | OSX | Windows | Code coverage | Matrix | -----------------|-------|-----|---------| ------------- |--------| -Develop: | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=develop&env=BADGE=linux)](https://travis-ci.org/klemens-morgenstern/boost-process) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=develop&build=linux)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=linux) | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=develop&env=BADGE=osx)](https://travis-ci.org/klemens-morgenstern/boost-process) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=develop&build=osx)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=osx) | [![Build status](https://ci.appveyor.com/api/projects/status/peup7e6m0e1bb5ba/branch/develop?svg=true)](https://ci.appveyor.com/project/klemens-morgenstern/boost-process/branch/develop) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=develop&build=windows)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=develop&build=windows) | [![Coverage Status](https://coveralls.io/repos/github/klemens-morgenstern/boost-process/badge.svg?branch=develop)](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=develop) | [![Matrix](https://img.shields.io/badge/matrix-develop-lightgray.svg)](http://www.boost.org/development/tests/develop/developer/process.html) -Master: | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=master&env=BADGE=linux)](https://travis-ci.org/klemens-morgenstern/boost-process) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=master&build=linux)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=linux) | [![Build Status](https://travis-ci.org/klemens-morgenstern/boost-process.svg?branch=master&env=BADGE=osx)](https://travis-ci.org/klemens-morgenstern/boost-process) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=master&build=osx)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=osx) | [![Build status](https://ci.appveyor.com/api/projects/status/peup7e6m0e1bb5ba/branch/master?svg=true)](https://ci.appveyor.com/project/klemens-morgenstern/boost-process/branch/master) [![badge](https://api.report.ci/status/klemens-morgenstern/boost-process/badge.svg?branch=master&build=windows)](https://api.report.ci/status/klemens-morgenstern/boost-process?branch=master&build=windows) | [![Coverage Status](https://coveralls.io/repos/github/klemens-morgenstern/boost-process/badge.svg?branch=master)](https://coveralls.io/github/klemens-morgenstern/boost-process?branch=master) | [![Matrix](https://img.shields.io/badge/matrix-master-lightgray.svg)](http://www.boost.org/development/tests/master/developer/process.html) +| Branches | Linux / Windows | Code coverage | Matrix | +|----------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| +| Develop: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/develop/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-develop-lightgray.svg)](http://www.boost.org/development/tests/develop/developer/process.html) | +| Master: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg?ref=refs/heads/develop)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/master/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-master-lightgray.svg)](http://www.boost.org/development/tests/master/developer/process.html) | -[Open Issues](https://github.com/klemens-morgenstern/boost-process/issues) -[Latest developer documentation](http://klemens-morgenstern.github.io/process/) + + +[Open Issues](https://github.com/boostorg/process/issues) + +[Latest developer documentation](https://www.boost.org/doc/libs/develop/doc/html/process.html) ### About This C++11 library is the current result of a long attempt to get a boost.process library, which is going on since 2006. From 0733217423661ffcc62f5cd6a00ef3e088ca2450 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 19 May 2022 19:20:53 +0800 Subject: [PATCH 045/397] Added boost_process_ prefix to test/CMakeFiles.txt. --- test/CMakeLists.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eaf671da1..9b926196f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,18 +1,18 @@ enable_testing() -add_executable(sparring_partner sparring_partner.cpp ) -target_link_libraries(sparring_partner Boost::program_options Boost::filesystem Boost::iostreams) +add_executable(boost_process_sparring_partner sparring_partner.cpp ) +target_link_libraries(boost_process_sparring_partner Boost::program_options Boost::filesystem Boost::iostreams) -add_executable(exit_argc exit_argc.cpp) +add_executable(boost_process_exit_argc exit_argc.cpp) -add_executable(sub_launch sub_launcher.cpp) -target_link_libraries(sparring_partner Boost::program_options Boost::filesystem Boost::iostreams Boost::system) +add_executable(boost_process_sub_launch sub_launcher.cpp) +target_link_libraries(boost_process_sub_launch Boost::program_options Boost::filesystem Boost::iostreams Boost::system) function(process_standalone_test name ) - add_executable(${name} ${name}.cpp) - target_link_libraries(${name} Boost::system Boost::filesystem) - add_test(NAME ${name} COMMAND $ ) + add_executable(boost_process_${name} ${name}.cpp) + target_link_libraries(boost_process_${name} Boost::system Boost::filesystem) + add_test(NAME boost_process_${name} COMMAND $ ) endfunction() process_standalone_test(environment) @@ -20,18 +20,18 @@ process_standalone_test(async_pipe) process_standalone_test(pipe) function(process_sub_launch_test name ) - add_executable(${name} ${name}.cpp) - target_link_libraries(${name} Boost::system Boost::filesystem Boost::thread) - add_test(NAME ${name} COMMAND $ $ ) + add_executable(boost_process_${name} ${name}.cpp) + target_link_libraries(boost_process_${name} Boost::system Boost::filesystem Boost::thread) + add_test(NAME boost_process_${name} COMMAND $ $ ) endfunction() process_sub_launch_test(group) process_sub_launch_test(group_wait) function(process_sparring_partner_launch name ) - add_executable(${name} ${name}.cpp) - target_link_libraries(${name} Boost::system Boost::filesystem Boost::thread) - add_test(NAME ${name} COMMAND $ $ ) + add_executable(boost_process_${name} ${name}.cpp) + target_link_libraries(boost_process_${name} Boost::system Boost::filesystem Boost::thread) + add_test(NAME boost_process_${name} COMMAND $ $ ) endfunction() process_sparring_partner_launch(async) From 4943c74e8e1e9400adecc83b85ec203372d99145 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 19 May 2022 00:17:14 +0800 Subject: [PATCH 046/397] First steps for v2 - Added utility functions - cstring_ref - codecvt functions --- include/boost/process/v2/cstring_ref.hpp | 221 +++++++ include/boost/process/v2/detail/codecvt.hpp | 540 ++++++++++++++++++ include/boost/process/v2/detail/config.hpp | 86 +++ .../boost/process/v2/detail/impl/codecvt.ipp | 149 +++++ .../process/v2/detail/impl/last_error.ipp | 48 ++ .../process/v2/detail/impl/throw_error.ipp | 31 + .../boost/process/v2/detail/last_error.hpp | 28 + .../boost/process/v2/detail/throw_error.hpp | 32 ++ .../process/v2/detail/throw_exception.hpp | 39 ++ include/boost/process/v2/error.hpp | 28 + include/boost/process/v2/impl/error.ipp | 55 ++ include/boost/process/v2/src.hpp | 21 + test/v2/CMakeLists.txt | 14 + test/v2/codecvt.cpp | 69 +++ test/v2/cstring_ref.cpp | 99 ++++ test/v2/test_impl.cpp | 11 + 16 files changed, 1471 insertions(+) create mode 100644 include/boost/process/v2/cstring_ref.hpp create mode 100644 include/boost/process/v2/detail/codecvt.hpp create mode 100644 include/boost/process/v2/detail/config.hpp create mode 100644 include/boost/process/v2/detail/impl/codecvt.ipp create mode 100644 include/boost/process/v2/detail/impl/last_error.ipp create mode 100644 include/boost/process/v2/detail/impl/throw_error.ipp create mode 100644 include/boost/process/v2/detail/last_error.hpp create mode 100644 include/boost/process/v2/detail/throw_error.hpp create mode 100644 include/boost/process/v2/detail/throw_exception.hpp create mode 100644 include/boost/process/v2/error.hpp create mode 100644 include/boost/process/v2/impl/error.ipp create mode 100644 include/boost/process/v2/src.hpp create mode 100644 test/v2/CMakeLists.txt create mode 100644 test/v2/codecvt.cpp create mode 100644 test/v2/cstring_ref.cpp create mode 100644 test/v2/test_impl.cpp diff --git a/include/boost/process/v2/cstring_ref.hpp b/include/boost/process/v2/cstring_ref.hpp new file mode 100644 index 000000000..47ab541cd --- /dev/null +++ b/include/boost/process/v2/cstring_ref.hpp @@ -0,0 +1,221 @@ +// 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_CSTRING_REF_HPP +#define BOOST_PROCESS_V2_CSTRING_REF_HPP + +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#else +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +BOOST_CONSTEXPR static const char* null_char_(char) {return "";} +BOOST_CONSTEXPR static const wchar_t* null_char_(wchar_t) {return L"";} +BOOST_CONSTEXPR static const char16_t* null_char_(char16_t) {return u"";} +BOOST_CONSTEXPR static const char32_t* null_char_(char32_t) {return U"";} + +#if defined(BOOST_PROCESS_V2_HAS_CHAR8_T) +BOOST_CONSTEXPR static const char8_t* null_char_(char8_t) {return u8"";} +#endif + + +} + +#if defined(BOOST_PROCESS_V2_STANDALONE) +using std::basic_string_view; +using std:: string_view; +using std:: wstring_view; +using std::u16string_view; +using std::u32string_view; +#else +using boost::basic_string_view; +using boost:: string_view; +using boost:: wstring_view; +using boost::u16string_view; +using boost::u32string_view; +#endif + +template> +struct basic_cstring_ref +{ + using value_type = CharT; + using traits_type = Traits; + + BOOST_CONSTEXPR basic_cstring_ref() : view_(detail::null_char_(value_type{})) {} + BOOST_CONSTEXPR basic_cstring_ref(std::nullptr_t) = delete; + + BOOST_CONSTEXPR basic_cstring_ref( const value_type* s ) : view_(s) {} + + template().c_str())>::type + >::value>::type> + BOOST_CONSTEXPR basic_cstring_ref(Source && src) : view_(src.c_str()) {} + + BOOST_CONSTEXPR typename std::basic_string_view::const_pointer c_str() const BOOST_NOEXCEPT + { + return this->data(); + } + + using string_view_type = basic_string_view; + constexpr operator string_view_type() const {return view_;} + + using pointer = CharT *; + using const_pointer = const CharT *; + using reference = CharT &; + using const_reference = const CharT &; + using const_iterator = const_pointer; + using iterator = const_iterator; + using const_reverse_iterator = typename std::reverse_iterator; + using reverse_iterator = typename std::reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + static BOOST_CONSTEXPR size_type npos = -1; + + BOOST_CONSTEXPR const_iterator begin() const BOOST_NOEXCEPT {return view_;}; + BOOST_CONSTEXPR const_iterator end() const BOOST_NOEXCEPT {return view_ + length();}; + BOOST_CONSTEXPR const_iterator cbegin() const BOOST_NOEXCEPT {return view_;}; + BOOST_CONSTEXPR const_iterator cend() const BOOST_NOEXCEPT {return view_ + length();}; + BOOST_CONSTEXPR const_reverse_iterator rbegin() const BOOST_NOEXCEPT {return reverse_iterator(view_ + length());}; + BOOST_CONSTEXPR const_reverse_iterator rend() const BOOST_NOEXCEPT {return reverse_iterator(view_);}; + BOOST_CONSTEXPR const_reverse_iterator crbegin() const BOOST_NOEXCEPT {return reverse_iterator(view_ + length());}; + BOOST_CONSTEXPR const_reverse_iterator crend() const BOOST_NOEXCEPT {return reverse_iterator(view_);}; + + BOOST_CONSTEXPR size_type size() const BOOST_NOEXCEPT {return length(); } + BOOST_CONSTEXPR size_type length() const BOOST_NOEXCEPT {return traits_type::length(view_); } + BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT {return std::numeric_limits::max() / sizeof(CharT); } + BOOST_ATTRIBUTE_NODISCARD BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT {return *view_ == *detail::null_char_(CharT{}); } + + BOOST_CONSTEXPR const_reference operator[](size_type pos) const {return view_[pos] ;} + BOOST_CONSTEXPR const_reference at(size_type pos) const + { + if (pos >= size()) + throw std::out_of_range("cstring-view out of range"); + return view_[pos]; + } + BOOST_CONSTEXPR const_reference front() const {return *view_;} + BOOST_CONSTEXPR const_reference back() const {return view_[length() - 1];} + BOOST_CONSTEXPR const_pointer data() const BOOST_NOEXCEPT {return view_;} + BOOST_CXX14_CONSTEXPR void remove_prefix(size_type n) {view_ = view_ + n;} + BOOST_CONSTEXPR void swap(basic_cstring_ref& s) BOOST_NOEXCEPT {std::swap(view_, s.view_);} + + size_type copy(value_type* s, size_type n, size_type pos = 0) const + { + return traits_type::copy(s, view_ + pos, n) - view_; + } + BOOST_CONSTEXPR basic_cstring_ref substr(size_type pos = 0) const + { + return basic_cstring_ref(view_ + pos); + } + BOOST_CXX14_CONSTEXPR string_view_type substr(size_type pos , size_type n) const + { + return string_view_type(view_ + pos, std::min(n, length() - pos)); + } + + BOOST_CXX14_CONSTEXPR int compare(basic_cstring_ref x) const BOOST_NOEXCEPT + { + auto idx = 0u; + for (; view_[idx] != null_char_()[0] && x[idx] != null_char_()[0]; idx++) + if (!traits_type::eq(view_[idx], x[idx])) + return traits_type::lt(view_[idx], x[idx]) ? -1 : 1; + + return traits_type::to_int_type(view_[idx]) - + traits_type::to_int_type(x[idx]); // will compare to null char of either. + } + + BOOST_CONSTEXPR bool starts_with(string_view_type x) const BOOST_NOEXCEPT + { + if (x.empty()) + return true; + + auto idx = 0u; + for (; view_[idx] != null_char_()[0] && idx < x.size(); idx++) + if (!traits_type::eq(view_[idx], x[idx])) + return false; + + return idx == x.size() || view_[idx] != null_char_()[0]; + } + BOOST_CONSTEXPR bool starts_with(value_type x) const BOOST_NOEXCEPT + { + return traits_type::eq(view_[0], x); + } + + friend BOOST_CXX14_CONSTEXPR bool operator==(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT + { + std::size_t idx = 0u; + for (idx = 0u; x[idx] != null_char_()[0] && y[idx] != null_char_()[0]; idx++) + if (!traits_type::eq(x[idx], y[idx])) + return false; + return x[idx] == y[idx]; + } + friend BOOST_CXX14_CONSTEXPR bool operator!=(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT + { + std::size_t idx = 0u; + for (idx = 0u; x[idx] != null_char_()[0] && + y[idx] != null_char_()[0]; idx++) + if (!traits_type::eq(x[idx], y[idx])) + return true; + return x[idx] != y[idx]; + } + friend BOOST_CXX14_CONSTEXPR bool operator< (basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) < 0;} + friend BOOST_CXX14_CONSTEXPR bool operator> (basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) > 0;} + friend BOOST_CXX14_CONSTEXPR bool operator<=(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) <= 0;} + friend BOOST_CXX14_CONSTEXPR bool operator>=(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) >= 0;} + + // modifiers + void clear() BOOST_NOEXCEPT { view_ = null_char_(); } // Boost extension + + std::basic_string to_string() const { + return std::basic_string(begin(), end()); + } + + template + std::basic_string to_string(const Allocator& a) const { + return std::basic_string(begin(), end(), a); + } + + private: + BOOST_CONSTEXPR static const_pointer null_char_() {return detail::null_char_(CharT{});} + const_pointer view_; +}; + +template +inline std::basic_ostream& +operator<<(std::basic_ostream& os, + const basic_cstring_ref& str) +{ + return os << static_cast>(str); +} + +// Forward declaration of Boost.ContainerHash function +template std::size_t hash_range(It, It); + +template +std::size_t hash_value(basic_string_view s) { + return boost::hash_range(s.begin(), s.end()); +} + +using cstring_ref = basic_cstring_ref; +using wcstring_ref = basic_cstring_ref; +using u16cstring_ref = basic_cstring_ref; +using u32cstring_ref = basic_cstring_ref; + +#if defined(BOOST_PROCESS_V2_HAS_CHAR8_T) +using u8cstring_ref = basic_cstring_ref; +#endif + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_CSTRING_REF_HPP diff --git a/include/boost/process/v2/detail/codecvt.hpp b/include/boost/process/v2/detail/codecvt.hpp new file mode 100644 index 000000000..4e49385ff --- /dev/null +++ b/include/boost/process/v2/detail/codecvt.hpp @@ -0,0 +1,540 @@ +// Copyright (c) 2021 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_DETAIL_CODECVT_HPP +#define BOOST_PROCESS_V2_DETAIL_CODECVT_HPP + +#include +#include +#include + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + + +namespace detail +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +BOOST_PROCESS_V2_DECL const std::codecvt< wchar_t, char, std::mbstate_t > & default_locale(); + +#else + +inline const std::codecvt< wchar_t, char, std::mbstate_t > & default_codecvt() +{ + return std::use_facet>(std::locale()); +} + +#endif + +// Needed conversions [char8_t, char16_t, char32_t, wchar_t, char] <-> [char, wchar_t] + +// C++20 + +//std::codecvt identity conversion +//std::codecvt conversion between the system's native wide and the single-byte narrow character sets + +//std::codecvt conversion between UTF-16 and UTF-8 (since C++20) +//std::codecvt conversion between UTF-32 and UTF-8 (since C++20) + + +// C++17 + +//std::codecvt identity conversion +//std::codecvt conversion between the system's native wide and the single-byte narrow character sets + +//std::codecvt conversion between UTF-16 and UTF-8 (since C++11)(deprecated in C++20) +//std::codecvt conversion between UTF-32 and UTF-8 (since C++11)(deprecated in C++20) + +template< class Traits, class Char, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &, + const Char * begin, + const Char * end, + Char, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + return std::basic_string(begin, end, alloc); +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const wchar_t * begin, + const wchar_t * end, + char, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = loc == std::locale() + ? default_codecvt() + : std::use_facet >(loc) + ; + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = (end - begin) * 2; + std::basic_string tmp(len, ' ', alloc); + + auto itr = begin; + auto out_itr = tmp.data(); + auto e = f.out(mb, begin, end, itr, tmp.data(), tmp.data() + tmp.size(), out_itr); + ec.assign(e, error::get_codecvt_category()); + tmp.resize(out_itr - tmp.data()); + return tmp; +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const char * begin, + const char * end, + wchar_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = loc == std::locale() + ? default_codecvt() + : std::use_facet >(loc); + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = f.length(mb, begin, end, std::numeric_limits::max()); + + std::basic_string res(len, L' ', alloc); + auto itr = begin; + auto out_itr = res.data(); + auto e = f.in(mb, begin, end, itr, res.data(), res.data() + res.size(), out_itr); + ec.assign(e, error::get_codecvt_category()); + res.resize(out_itr - res.data()); + return res; +} + + +#if defined(BOOST_PROCESS_V2_HAS_CHAR8_T) + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &, + const char * begin, + const char * end, + char8_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + return std::basic_string(begin, end, alloc); +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &, + const char8_t * begin, + const char8_t * end, + char, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + return std::basic_string(begin, end, alloc); +} + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const char8_t * begin, + const char8_t * end, + wchar_t w, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + return convert_chars(ec, + reinterpret_cast(begin), + reinterpret_cast(end), w, alloc, loc); +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const wchar_t * begin, + const wchar_t * end, + char8_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = loc == std::locale() + ? default_codecvt() + : std::use_facet >(loc) + ; + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = (end - begin) * 2; + std::basic_string tmp(len, ' ', alloc); + + auto itr = begin; + auto out_itr = tmp.data(); + auto e = f.out(mb, begin, end, itr, + reinterpret_cast(tmp.data()), + reinterpret_cast(tmp.data() + tmp.size()), + reinterpret_cast(out_itr)); + ec.assign(e, error::get_codecvt_category()); + tmp.resize(out_itr - tmp.data()); + + return tmp; +} + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const char16_t * begin, + const char16_t * end, + char, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = std::use_facet >(loc); + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = (end - begin) * 2; + std::basic_string tmp(len, ' ', alloc); + + auto itr = begin; + auto out_itr = tmp.data(); + auto e = f.out(mb, begin, end, itr, + reinterpret_cast(tmp.data()), + reinterpret_cast(tmp.data() + tmp.size()), + reinterpret_cast(out_itr)); + + ec.assign(e, error::get_codecvt_category()); + tmp.resize(out_itr - tmp.data()); + + return tmp; +} + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const char32_t * begin, + const char32_t * end, + char, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = std::use_facet >(loc); + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = (end - begin) * 4; + std::basic_string tmp(len, ' ', alloc); + + auto itr = begin; + auto out_itr = tmp.data(); + auto e = f.out(mb, begin, end, itr, + reinterpret_cast(tmp.data()), + reinterpret_cast(tmp.data() + tmp.size()), + reinterpret_cast(out_itr)); + ec.assign(e, error::get_codecvt_category()); + + tmp.resize(out_itr - tmp.data()); + + return tmp; +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &ec, + const char * begin, + const char * end, + char16_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = std::use_facet >(loc); + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = f.length(mb, reinterpret_cast(begin), + reinterpret_cast(end), + std::numeric_limits::max()); + + std::basic_string res(len, u' ', alloc); + auto itr = begin; + auto out_itr = res.data(); + auto e = f.in(mb, + reinterpret_cast(begin), + reinterpret_cast(end), + reinterpret_cast(itr), + res.data(), res.data() + res.size(), out_itr); + ec.assign(e, error::get_codecvt_category()); + res.resize(out_itr - res.data()); + return res; +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const char * begin, + const char * end, + char32_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = std::use_facet >(loc) + ; + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = f.length(mb, + reinterpret_cast(begin), + reinterpret_cast(end), + std::numeric_limits::max()); + + std::basic_string res(len, U' ', alloc); + auto itr = begin; + auto out_itr = res.data(); + auto e = f.in(mb, + reinterpret_cast(begin), + reinterpret_cast(end), + reinterpret_cast(itr), + res.data(), res.data() + res.size(), out_itr); + ec.assign(e, error::get_codecvt_category()); + + res.resize(out_itr - res.data()); + return res; +} + + +#else + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const char16_t * begin, + const char16_t * end, + char, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = std::use_facet >(loc); + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = (end - begin) * 2; + std::basic_string tmp(len, ' ', alloc); + + auto itr = begin; + auto out_itr = tmp.data(); + auto e = f.out(mb, begin, end, itr, tmp.data(), tmp.data() + tmp.size(), out_itr); + + ec.assign(e, error::get_codecvt_category()); + tmp.resize(out_itr - tmp.data()); + + return tmp; +} + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const char32_t * begin, + const char32_t * end, + char, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = std::use_facet >(loc); + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = (end - begin) * 4; + std::basic_string tmp(len, ' ', alloc); + + auto itr = begin; + auto out_itr = tmp.data(); + auto e = f.out(mb, begin, end, itr, tmp.data(), tmp.data() + tmp.size(), out_itr); + ec.assign(e, error::get_codecvt_category()); + + tmp.resize(out_itr - tmp.data()); + + return tmp; +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &ec, + const char * begin, + const char * end, + char16_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = std::use_facet >(loc); + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = f.length(mb, begin, end, std::numeric_limits::max()); + + std::basic_string res(len, u' ', alloc); + auto itr = begin; + auto out_itr = res.data(); + auto e = f.in(mb, begin, end, itr, res.data(), res.data() + res.size(), out_itr); + ec.assign(e, error::get_codecvt_category()); + res.resize(out_itr - res.data()); + return res; +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code & ec, + const char * begin, + const char * end, + char32_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + const auto & f = std::use_facet >(loc) + ; + + std::mbstate_t mb = std::mbstate_t(); + const std::size_t len = f.length(mb, begin, end, std::numeric_limits::max()); + + std::basic_string res(len, U' ', alloc); + auto itr = begin; + auto out_itr = res.data(); + auto e = f.in(mb, begin, end, itr, res.data(), res.data() + res.size(), out_itr); + ec.assign(e, error::get_codecvt_category()); + + res.resize(out_itr - res.data()); + return res; +} + +#endif + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &ec, + const wchar_t * begin, + const wchar_t * end, + char16_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + using rebind_alloc = typename std::allocator_traits::template rebind_alloc; + auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); + + if (ec) + return u""; + + return convert_chars(ec, tmp.data(), tmp.data() + tmp.size(), u' ', alloc, loc); +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &ec, + const wchar_t * begin, + const wchar_t * end, + char32_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + using rebind_alloc = typename std::allocator_traits::template rebind_alloc; + auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); + if (ec) + return U""; + return convert_chars(ec, tmp.data(), tmp.data() + tmp.size(), U' ', alloc, loc); +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &ec, + const char16_t * begin, + const char16_t * end, + wchar_t, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + using rebind_alloc = typename std::allocator_traits::template rebind_alloc; + auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); + if (ec) + return L""; + return convert_chars(ec, tmp.data(), tmp.data() + tmp.size(), L' ', alloc, loc); +} + + +template< class Traits, class Alloc = std::allocator> +inline std::basic_string convert_chars( + error_code &ec, + const char32_t * begin, + const char32_t * end, + wchar_t w, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + using rebind_alloc = typename std::allocator_traits::template rebind_alloc; + auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); + if (ec) + return L""; + return convert_chars(ec, tmp.data(), tmp.data() + tmp.size(), L' ', alloc, loc); +} + + + +template< class Traits, class CharIn, class CharOut, class Alloc = std::allocator> +inline std::basic_string, Alloc> convert_chars( + const CharIn * begin, + const CharIn * end, + CharOut c, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + error_code ec; + auto res = convert_chars(ec, begin, end, c, alloc, loc); + if (ec) + detail::throw_error(ec, "convert_chars"); + return res; +} + +template< class CharIn, class CharOut, class Alloc = std::allocator> +inline std::basic_string, Alloc> convert_chars( + error_code & ec, + const CharIn * begin, + const CharIn * end, + CharOut c, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + return convert_chars>(ec, begin, end, c, alloc, loc); +} + +template< class CharIn, class CharOut, class Alloc = std::allocator> +inline std::basic_string, Alloc> convert_chars( + const CharIn * begin, + const CharIn * end, + CharOut c, + const Alloc & alloc = Alloc(), + const std::locale &loc = std::locale()) +{ + error_code ec; + auto res = convert_chars>(ec, begin, end, c, alloc, loc); + if (ec) + detail::throw_error(ec, "convert_chars"); + return res; +} + + + +} // detail + + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + +#endif //BOOST_PROCESS_V2_DETAIL_CODECVT_HPP diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp new file mode 100644 index 000000000..9e62ab27a --- /dev/null +++ b/include/boost/process/v2/detail/config.hpp @@ -0,0 +1,86 @@ +// 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_DETAIL_CONFIG_HPP +#define BOOST_PROCESS_V2_DETAIL_CONFIG_HPP + +#if defined(BOOST_PROCESS_V2_STANDALONE) + +#include +#include + +#if defined(ASIO_WINDOWS) +#define BOOST_PROCESS_V2_WINDOWS 1 +#endif + +#if defined(ASIO_HAS_UNISTD_H) +#define BOOST_PROCESS_V2_POSIX 1 +#endif + +#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace process_v2 { +#define BOOST_PROCESS_V2_END_NAMESPACE } + +#else +#include +#include +#include +#include + +#if defined(BOOST_ASIO_WINDOWS) +#define BOOST_PROCESS_V2_WINDOWS 1 +#endif + +#if defined(BOOST_ASIO_HAS_UNISTD_H) +#define BOOST_PROCESS_V2_POSIX 1 +#endif + +#define BOOST_PROCESS_V2_BEGIN_NAMESPACE \ +namespace boost { namespace process { namespace v2 { + +#define BOOST_PROCESS_V2_END_NAMESPACE } } } + +#endif + +#if !defined(BOOST_PROCESS_HAS_CHAR8_T) +# if (__cplusplus >= 202002) +# define BOOST_PROCESS_HAS_CHAR8_T 1 +# endif +#endif + + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(BOOST_PROCESS_STANDALONE) + +using std::error_code ; +using std::error_category ; +using std::system_category ; +using std::system_error ; + +#else + +using boost::system::error_code ; +using boost::system::error_category ; +using boost::system::system_category ; +using boost::system::system_error ; + +#endif + +BOOST_PROCESS_V2_END_NAMESPACE + +#ifndef BOOST_PROCESS_V2_HEADER_ONLY +# ifndef BOOST_PROCESS_V2_SEPARATE_COMPILATION +# define BOOST_PROCESS_V2_HEADER_ONLY 1 +# endif +#endif + +#if BOOST_PROCESS_V2_DOXYGEN +# define BOOST_PROCESS_V2_DECL +#elif defined(BOOST_PROCESS_V2_HEADER_ONLY) +# define BOOST_PROCESS_V2_DECL inline +#else +# define BOOST_PROCESS_V2_DECL +#endif + +#endif //BOOST_PROCESS_V2_DETAIL_CONFIG_HPP diff --git a/include/boost/process/v2/detail/impl/codecvt.ipp b/include/boost/process/v2/detail/impl/codecvt.ipp new file mode 100644 index 000000000..82eaf33dd --- /dev/null +++ b/include/boost/process/v2/detail/impl/codecvt.ipp @@ -0,0 +1,149 @@ +// 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_DETAIL_IMPL_CODECVT_IPP +#define BOOST_PROCESS_V2_DETAIL_IMPL_CODECVT_IPP + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +#include +#include + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace detail { + + +//copied from boost.filesystem +class windows_codecvt + : public std::codecvt< wchar_t, char, std::mbstate_t > +{ + public: + explicit windows_codecvt(std::size_t ref_count = 0) + : std::codecvt(ref_count) {} + protected: + + bool do_always_noconv() const noexcept override { return false; } + + // seems safest to assume variable number of characters since we don't + // actually know what codepage is active + int do_encoding() const noexcept override { return 0; } + + std::codecvt_base::result do_in(std::mbstate_t& state, + const char* from, const char* from_end, const char*& from_next, + wchar_t* to, wchar_t* to_end, wchar_t*& to_next) const override + { + + auto codepage = +#if !defined(BOOST_NO_ANSI_APIS) + ::AreFileApisANSI() ? CP_ACP : +#endif + CP_OEMCP; + + result res = ok; + int count = 0; + if ((count = ::MultiByteToWideChar(codepage, MB_PRECOMPOSED, + from, static_cast(from_end - from), + to, static_cast(to_end - to))) == 0) + { + switch (::GetLastError()) + { + case ERROR_INSUFFICIENT_BUFFER: + // A supplied buffer size was not large enough, or it was incorrectly set to NULL. + res = partial; + break; + case ERROR_INVALID_FLAGS: + // The values supplied for flags were not valid. + res = error; + break; + case ERROR_INVALID_PARAMETER: + // Any of the parameter values was invalid. + res = error; + break; + case ERROR_NO_UNICODE_TRANSLATION: + // Invalid Unicode was found in a string. + res = error; + break; + } + } + if (res != error) + { + from_next = from_end; + to_next = to + count; + *to_next = L'\0'; + } + return res; + } + + std::codecvt_base::result do_out(std::mbstate_t & state, + const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, + char* to, char* to_end, char*& to_next) const override + { + auto codepage = +#if !defined(BOOST_NO_ANSI_APIS) + ::AreFileApisANSI() ? CP_ACP : +#endif + CP_OEMCP; + result res = ok; + + int count = 0; + + + if ((count = WideCharToMultiByte(codepage, + WC_NO_BEST_FIT_CHARS, from, + static_cast(from_end - from), to, static_cast(to_end - to), 0, 0)) == 0) + { + switch (::GetLastError()) + { + case ERROR_INSUFFICIENT_BUFFER: + // A supplied buffer size was not large enough, or it was incorrectly set to NULL. + res = partial; + break; + case ERROR_INVALID_FLAGS: + // The values supplied for flags were not valid. + res = error; + break; + case ERROR_INVALID_PARAMETER: + // Any of the parameter values was invalid. + res = error; + break; + case ERROR_NO_UNICODE_TRANSLATION: + // Invalid Unicode was found in a string. + res = error; + break; + } } + if (res != error) + { + from_next = from_end; + to_next = to + count; + *to_next = '\0'; + } + return res; + } + + std::codecvt_base::result do_unshift(std::mbstate_t&, + char* /*from*/, char* /*to*/, char* & /*next*/) const override { return ok; } + + int do_length(std::mbstate_t&, + const char* from, const char* from_end, std::size_t /*max*/) const override + { + return std::distance(from, from_end); + } + + int do_max_length() const noexcept override { return 0; } + }; + +BOOST_PROCESS_V2_DECL const std::codecvt< wchar_t, char, std::mbstate_t > & default_codecvt() +{ + const static const windows_codecvt cvt{1}; + return cvt; +} + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif + +#endif //BOOST_PROCESS_V2_DETAIL_IMPL_CODECVT_IPP diff --git a/include/boost/process/v2/detail/impl/last_error.ipp b/include/boost/process/v2/detail/impl/last_error.ipp new file mode 100644 index 000000000..f9c294c25 --- /dev/null +++ b/include/boost/process/v2/detail/impl/last_error.ipp @@ -0,0 +1,48 @@ +// 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_DETAIL_IMPL_LAST_ERROR_IPP +#define BOOST_PROCESS_V2_DETAIL_IMPL_LAST_ERROR_IPP + +#include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace detail +{ + +error_code get_last_error() +{ +#if defined(BOOST_PROCESS_V2_WINDOWS) + return error_code(::GetLastError(), system_category()); +#else + return error_code(errno, system_category()); +#endif + +} + +void throw_last_error() +{ + throw system_error(get_last_error()); +} +void throw_last_error(const char * msg) +{ + throw system_error(get_last_error(), msg); +} +void throw_last_error(const std::string & msg) +{ + throw system_error(get_last_error(), msg); +} + + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_DETAIL_IMPL_LAST_ERROR_IPP \ No newline at end of file diff --git a/include/boost/process/v2/detail/impl/throw_error.ipp b/include/boost/process/v2/detail/impl/throw_error.ipp new file mode 100644 index 000000000..663bb1674 --- /dev/null +++ b/include/boost/process/v2/detail/impl/throw_error.ipp @@ -0,0 +1,31 @@ +// 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_DETAIL_IMPL_THROW_ERROR_IPP +#define BOOST_PROCESS_V2_DETAIL_IMPL_THROW_ERROR_IPP + +#include +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace detail +{ + +void do_throw_error(const error_code& err) +{ + system_error e(err); + throw_exception(e); +} + +void do_throw_error(const error_code& err, const char* location) +{ + system_error e(err, location); + throw_exception(e); +} + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_DETAIL_IMPL_THROW_ERROR_IPP diff --git a/include/boost/process/v2/detail/last_error.hpp b/include/boost/process/v2/detail/last_error.hpp new file mode 100644 index 000000000..1eba544e8 --- /dev/null +++ b/include/boost/process/v2/detail/last_error.hpp @@ -0,0 +1,28 @@ +// 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_DETAIL_LAST_ERROR_HPP +#define BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace detail { + +BOOST_PROCESS_V2_DECL error_code get_last_error(); +BOOST_PROCESS_V2_DECL void throw_last_error(); +BOOST_PROCESS_V2_DECL void throw_last_error(const char * msg); +BOOST_PROCESS_V2_DECL void throw_last_error(const std::string & msg); + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + + +#endif //BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP diff --git a/include/boost/process/v2/detail/throw_error.hpp b/include/boost/process/v2/detail/throw_error.hpp new file mode 100644 index 000000000..9d0d9867d --- /dev/null +++ b/include/boost/process/v2/detail/throw_error.hpp @@ -0,0 +1,32 @@ +// 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_DETAIL_THROW_ERROR_HPP +#define BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace detail +{ + +BOOST_PROCESS_V2_DECL void do_throw_error(const error_code& err); +BOOST_PROCESS_V2_DECL void do_throw_error(const error_code& err, const char* location); + +inline void throw_error(const error_code& err) +{ + if (err) + do_throw_error(err); +} + +inline void throw_error(const error_code& err, const char* location) +{ + if (err) + do_throw_error(err, location); +} + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP diff --git a/include/boost/process/v2/detail/throw_exception.hpp b/include/boost/process/v2/detail/throw_exception.hpp new file mode 100644 index 000000000..0ef29d08f --- /dev/null +++ b/include/boost/process/v2/detail/throw_exception.hpp @@ -0,0 +1,39 @@ +// 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_DETAIL_THROW_EXCEPTION_HPP +#define BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP + +#include + +#if !defined(BOOST_PROCESS_V2_STANDALONE) + +#include + +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +#if defined(BOOST_PROCESS_V2_STANDALONE) + +template +inline void throw_exception(const Exception& e) +{ + throw e; +} + +#else + +using boost::throw_exception; + +#endif + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP diff --git a/include/boost/process/v2/error.hpp b/include/boost/process/v2/error.hpp new file mode 100644 index 000000000..54451488f --- /dev/null +++ b/include/boost/process/v2/error.hpp @@ -0,0 +1,28 @@ +// Copyright (c) 2021 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_ERROR_HPP +#define BOOST_PROCESS_V2_ERROR_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace error +{ + +extern BOOST_PROCESS_V2_DECL const error_category& get_codecvt_category(); +static const error_category& codecvt_category = error::get_codecvt_category(); + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + +#endif //BOOST_PROCESS_V2_ERROR_HPP diff --git a/include/boost/process/v2/impl/error.ipp b/include/boost/process/v2/impl/error.ipp new file mode 100644 index 000000000..2bc52b676 --- /dev/null +++ b/include/boost/process/v2/impl/error.ipp @@ -0,0 +1,55 @@ +// Copyright (c) 2021 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_IMPL_ERROR_IPP +#define BOOST_PROCESS_V2_IMPL_ERROR_IPP + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace error +{ + +namespace detail +{ + +// can be replaced with filesystem::codecvt_error_category in boost +struct codecvt_category : public error_category +{ + codecvt_category() : error_category(0xDAEDu) {} + + const char* name() const noexcept + { + return "process.v2.codecvt"; + } + std::string message(int value) const + { + if (value == std::codecvt_base::ok) + return "conversion completed without error."; + else if (value == std::codecvt_base::partial) + return "not enough space in the output buffer or unexpected end of source buffer"; + else if (value == std::codecvt_base::error) + return "encountered a character that could not be converted"; + else if (value == std::codecvt_base::noconv) + return "this facet is non-converting, no output written"; + return "process.v2.codecvt error"; + } +}; + + +} // namespace detail + +const error_category& get_codecvt_category() +{ + static detail::codecvt_category instance; + return instance; +} + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_IMPL_ERROR_IPP diff --git a/include/boost/process/v2/src.hpp b/include/boost/process/v2/src.hpp new file mode 100644 index 000000000..39072f526 --- /dev/null +++ b/include/boost/process/v2/src.hpp @@ -0,0 +1,21 @@ +// Copyright (c) 2021 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_SRC_HPP +#define BOOST_PROCESS_V2_SRC_HPP + +#define BOOST_PROCESS_V2_SOURCE + +#include + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) +# error Do not compile Beast library source with BOOST_BEAST_HEADER_ONLY defined +#endif + +#include +#include +#include +#include + +#endif //BOOST_PROCESS_V2_SRC_HPP diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt new file mode 100644 index 000000000..8b5656a7b --- /dev/null +++ b/test/v2/CMakeLists.txt @@ -0,0 +1,14 @@ + +add_library(boost_process_v2_test_impl test_impl.cpp) +target_link_libraries(boost_process_v2_test_impl Boost::unit_test_framework Boost::process) +target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1) +target_include_directories(boost_process_v2_test_impl PUBLIC ../../include) +enable_testing() + +add_executable(boost_process_v2_codecvt codecvt.cpp) +target_link_libraries(boost_process_v2_codecvt boost_process_v2_test_impl) +add_test(NAME boost_process_v2_codecvt COMMAND $) + +add_executable(boost_process_v2_cstring_ref cstring_ref.cpp) +target_link_libraries(boost_process_v2_cstring_ref boost_process_v2_test_impl) +add_test(NAME boost_process_v2_cstring_ref COMMAND $) \ No newline at end of file diff --git a/test/v2/codecvt.cpp b/test/v2/codecvt.cpp new file mode 100644 index 000000000..52f74a66a --- /dev/null +++ b/test/v2/codecvt.cpp @@ -0,0 +1,69 @@ +// 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) + +// +// detail/codecvt.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include + +#include + + +BOOST_AUTO_TEST_CASE(test_codecvt) +{ + auto end = [](const auto * c){return c + std::char_traits>::length(c);}; + + const char * in = "test-input"; + + const wchar_t * win_t = L"test-input"; + const char16_t * in16_t = u"test-input"; + const char32_t * in32_t = U"test-input"; + +#if defined(BOOST_PROCESS_HAS_CHAR8_T) + const char8_t in8_t[] = u8"test-input"; +#endif + + BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), ' ') == in); + BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), L' ') == win_t); + + BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), L' ') == win_t); + BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), ' ') == in); + + BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), u' ') == in16_t); + BOOST_CHECK(boost::process::v2::detail::convert_chars(in16_t, end(in16_t), ' ') == in); + + BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), U' ') == in32_t); + BOOST_CHECK(boost::process::v2::detail::convert_chars(in32_t, end(in32_t), ' ') == in); + + BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), u' ') == in16_t); + BOOST_CHECK(boost::process::v2::detail::convert_chars(in16_t, end(in16_t), L' ') == win_t); + + BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), U' ') == in32_t); + BOOST_CHECK(boost::process::v2::detail::convert_chars(in32_t, end(in32_t), L' ') == win_t); + +#if defined(BOOST_PROCESS_HAS_CHAR8_T) + BOOST_CHECK(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), u8' ') == in8_t); + + BOOST_CHECK(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), ' ') == in); + BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), u8' ') == in8_t); + + BOOST_CHECK_EQUAL(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), L' ') == win_t); + BOOST_CHECK_EQUAL(boost::process::v2::detail::convert_chars(win_t, end(win_t), u8' ') == in8_t); +#endif + +} diff --git a/test/v2/cstring_ref.cpp b/test/v2/cstring_ref.cpp new file mode 100644 index 000000000..d575d9540 --- /dev/null +++ b/test/v2/cstring_ref.cpp @@ -0,0 +1,99 @@ +// 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) + + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include + +#include + +namespace bp2 = boost::process::v2; + +template class bp2::basic_cstring_ref>; + +using char_type = bp2::basic_cstring_ref>::const_pointer; + +BOOST_AUTO_TEST_CASE(cstring_view_test) +{ + auto null = bp2::cstring_ref(); + BOOST_CHECK(null.empty()); + BOOST_CHECK_NE(null.c_str(), nullptr); + BOOST_CHECK_EQUAL(null.c_str()[0], '\0'); + BOOST_CHECK_EQUAL(null, ""); + auto *c = "foobar"; + bp2::cstring_ref cv = c; + + BOOST_CHECK_EQUAL(cv.c_str(), c); + + std::string s = "barfoo"; + + bp2::cstring_ref sv = s; + BOOST_CHECK(!s.empty()); + BOOST_CHECK_EQUAL(sv.c_str(), s.c_str()); + + BOOST_CHECK_EQUAL_COLLECTIONS(s.begin(), s.end(), sv.begin(), sv.end()); + BOOST_CHECK_EQUAL_COLLECTIONS(s.cbegin(), s.cend(), sv.cbegin(), sv.cend()); + BOOST_CHECK_EQUAL_COLLECTIONS(s.rbegin(), s.rend(), sv.rbegin(), sv.rend()); + BOOST_CHECK_EQUAL_COLLECTIONS(s.crbegin(), s.crend(), sv.crbegin(), sv.crend()); + + BOOST_CHECK_EQUAL(sv.size(), 6); + sv.remove_prefix(1); + BOOST_CHECK_EQUAL(sv.at(0), 'a'); + BOOST_CHECK_EQUAL(sv.at(4), 'o'); + BOOST_CHECK_THROW(sv.at(5), std::out_of_range); + BOOST_CHECK_EQUAL(sv.front(), 'a'); + BOOST_CHECK_EQUAL(sv.back(), 'o'); + + BOOST_CHECK_EQUAL(sv.length(), 5); + BOOST_CHECK_EQUAL(sv.c_str(), s.c_str() + 1); + auto cp = sv; + + BOOST_CHECK_EQUAL(sv.substr(2).c_str(), s.c_str() + 3); + + bp2::string_view ssv = sv; + BOOST_CHECK_EQUAL(ssv, "arfoo"); + + ssv = sv.substr(1, 3); + BOOST_CHECK_EQUAL(ssv, "rfo"); + + sv.swap(null); + + const bp2::cstring_ref cc = null; + BOOST_CHECK_EQUAL(cc.front(), 'a'); + BOOST_CHECK_EQUAL(cc.back(), 'o'); + BOOST_CHECK_GE(cc.max_size(), cc.size()); + + char out[2]; + cc.copy(out, 2, 1); + BOOST_CHECK_EQUAL(out[0], 'r'); + BOOST_CHECK_EQUAL(out[1], 'f'); + BOOST_CHECK(cc.starts_with('a')); + BOOST_CHECK(cc.starts_with("arf")); + BOOST_CHECK(cc == cc); + BOOST_CHECK(cc == null); + BOOST_CHECK(!(cc == sv)); + + BOOST_CHECK(!(cc != cc)); + BOOST_CHECK(!(cc != null)); + BOOST_CHECK(cc != sv); + + null.clear(); + BOOST_CHECK_EQUAL(null.to_string(), ""); + BOOST_CHECK_EQUAL(null.to_string(std::allocator()), ""); + + bp2::cstring_ref av = "aa", bv = "bb"; + BOOST_CHECK_LT(av, bv); + BOOST_CHECK_GT(bv, av); + + BOOST_CHECK_EQUAL(av.compare(av), 0); + BOOST_CHECK_LT(av.compare(bv), 0); + BOOST_CHECK_GT(bv.compare(av), 0); + +} \ No newline at end of file diff --git a/test/v2/test_impl.cpp b/test/v2/test_impl.cpp new file mode 100644 index 000000000..2abc39a59 --- /dev/null +++ b/test/v2/test_impl.cpp @@ -0,0 +1,11 @@ +// Copyright (c) 2021 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#define BOOST_TEST_MODULE process_test +#include + +#include + From e0e801cbb4a9d53e4ba9626db21784ecb6bdbe46 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 19 May 2022 00:30:27 +0800 Subject: [PATCH 047/397] Added the pid get_id function and pid_type type alias. --- include/boost/process/v2/impl/pid.ipp | 27 +++++++++++++++++++++ include/boost/process/v2/pid.hpp | 34 +++++++++++++++++++++++++++ include/boost/process/v2/src.hpp | 1 + test/v2/CMakeLists.txt | 6 ++++- test/v2/pid.cpp | 15 ++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 include/boost/process/v2/impl/pid.ipp create mode 100644 include/boost/process/v2/pid.hpp create mode 100644 test/v2/pid.cpp diff --git a/include/boost/process/v2/impl/pid.ipp b/include/boost/process/v2/impl/pid.ipp new file mode 100644 index 000000000..1ac40acb6 --- /dev/null +++ b/include/boost/process/v2/impl/pid.ipp @@ -0,0 +1,27 @@ +// 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_IMPL_PID_IPP +#define BOOST_PROCESS_V2_IMPL_PID_IPP + +#include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(BOOST_PROCESS_V2_WINDOWS) +pid_type current_pid() {return ::GetCurrentProcessId();} +#else +pid_type current_pid() {return ::getpid();} +#endif + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_IMPL_PID_IPP diff --git a/include/boost/process/v2/pid.hpp b/include/boost/process/v2/pid.hpp new file mode 100644 index 000000000..8ab1c9a34 --- /dev/null +++ b/include/boost/process/v2/pid.hpp @@ -0,0 +1,34 @@ +// 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_PID_HPP +#define BOOST_PROCESS_V2_PID_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +typedef unsigned long pid_type; + +#else + +typedef int pid_type; + +#endif + +/// Get the process id of the current process. +BOOST_PROCESS_V2_DECL pid_type current_pid(); + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + + +#endif //BOOST_PROCESS_V2_PID_HPP diff --git a/include/boost/process/v2/src.hpp b/include/boost/process/v2/src.hpp index 39072f526..01c3fd0c9 100644 --- a/include/boost/process/v2/src.hpp +++ b/include/boost/process/v2/src.hpp @@ -14,6 +14,7 @@ #endif #include +#include #include #include #include diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index 8b5656a7b..9839fd80f 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -11,4 +11,8 @@ add_test(NAME boost_process_v2_codecvt COMMAND $) \ No newline at end of file +add_test(NAME boost_process_v2_cstring_ref COMMAND $) + +add_executable(boost_process_v2_pid pid.cpp) +target_link_libraries(boost_process_v2_pid boost_process_v2_test_impl) +add_test(NAME boost_process_v2_pid COMMAND $) \ No newline at end of file diff --git a/test/v2/pid.cpp b/test/v2/pid.cpp new file mode 100644 index 000000000..13b72372a --- /dev/null +++ b/test/v2/pid.cpp @@ -0,0 +1,15 @@ +// 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) + +#include + +#include + + +BOOST_AUTO_TEST_CASE(test_pid) +{ + namespace bp2 = boost::process::v2; + BOOST_CHECK_NE(bp2::current_pid(), static_cast(0)); +} From dbcc946dac6f1f21839dcd4da61b3ae97bed2563 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 19 May 2022 00:34:52 +0800 Subject: [PATCH 048/397] Added early return for empty input. --- include/boost/process/v2/detail/codecvt.hpp | 51 ++++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/include/boost/process/v2/detail/codecvt.hpp b/include/boost/process/v2/detail/codecvt.hpp index 4e49385ff..81561397f 100644 --- a/include/boost/process/v2/detail/codecvt.hpp +++ b/include/boost/process/v2/detail/codecvt.hpp @@ -71,10 +71,12 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = loc == std::locale() ? default_codecvt() - : std::use_facet >(loc) - ; + : std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); const std::size_t len = (end - begin) * 2; @@ -98,6 +100,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = loc == std::locale() ? default_codecvt() : std::use_facet >(loc); @@ -166,6 +171,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = loc == std::locale() ? default_codecvt() : std::use_facet >(loc) @@ -196,6 +204,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); @@ -224,6 +235,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); @@ -253,6 +267,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); @@ -283,8 +300,10 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { - const auto & f = std::use_facet >(loc) - ; + if (begin == end) + return {}; + + const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); const std::size_t len = f.length(mb, @@ -318,6 +337,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); @@ -343,6 +365,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); @@ -369,6 +394,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); @@ -393,8 +421,10 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { - const auto & f = std::use_facet >(loc) - ; + if (begin == end) + return {}; + + const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); const std::size_t len = f.length(mb, begin, end, std::numeric_limits::max()); @@ -420,6 +450,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + using rebind_alloc = typename std::allocator_traits::template rebind_alloc; auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); @@ -439,6 +472,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + using rebind_alloc = typename std::allocator_traits::template rebind_alloc; auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); if (ec) @@ -456,6 +492,9 @@ inline std::basic_string convert_chars( const Alloc & alloc = Alloc(), const std::locale &loc = std::locale()) { + if (begin == end) + return {}; + using rebind_alloc = typename std::allocator_traits::template rebind_alloc; auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); if (ec) From 7bdf11f550275a9efcb2328949501c989c397bba Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 20 May 2022 01:32:47 +0800 Subject: [PATCH 049/397] Added posix first environment draft. --- include/boost/process/v2/cstring_ref.hpp | 14 +- include/boost/process/v2/detail/config.hpp | 29 +- .../process/v2/detail/environment_posix.hpp | 76 + .../process/v2/detail/environment_win.hpp | 194 +++ .../process/v2/detail/impl/environment.ipp | 18 + .../v2/detail/impl/environment_posix.ipp | 81 + .../v2/detail/impl/environment_win.ipp | 147 ++ include/boost/process/v2/environment.hpp | 1455 +++++++++++++++++ include/boost/process/v2/src.hpp | 3 +- test/CMakeLists.txt | 4 +- test/v2/CMakeLists.txt | 21 +- test/v2/environment.cpp | 116 ++ 12 files changed, 2140 insertions(+), 18 deletions(-) create mode 100644 include/boost/process/v2/detail/environment_posix.hpp create mode 100644 include/boost/process/v2/detail/environment_win.hpp create mode 100644 include/boost/process/v2/detail/impl/environment.ipp create mode 100644 include/boost/process/v2/detail/impl/environment_posix.ipp create mode 100644 include/boost/process/v2/detail/impl/environment_win.ipp create mode 100644 include/boost/process/v2/environment.hpp create mode 100644 test/v2/environment.cpp diff --git a/include/boost/process/v2/cstring_ref.hpp b/include/boost/process/v2/cstring_ref.hpp index 47ab541cd..b45545d60 100644 --- a/include/boost/process/v2/cstring_ref.hpp +++ b/include/boost/process/v2/cstring_ref.hpp @@ -119,9 +119,10 @@ struct basic_cstring_ref { return basic_cstring_ref(view_ + pos); } - BOOST_CXX14_CONSTEXPR string_view_type substr(size_type pos , size_type n) const + + BOOST_CONSTEXPR string_view_type substr(size_type pos, size_type length) const { - return string_view_type(view_ + pos, std::min(n, length() - pos)); + return string_view_type(view_).substr(pos, length); } BOOST_CXX14_CONSTEXPR int compare(basic_cstring_ref x) const BOOST_NOEXCEPT @@ -152,6 +153,15 @@ struct basic_cstring_ref return traits_type::eq(view_[0], x); } + BOOST_CONSTEXPR size_type find( CharT ch, size_type pos = 0 ) const BOOST_NOEXCEPT + { + for (auto p = view_ + pos; *p != *null_char_(); p++) + if (traits_type::eq(*p, ch)) + return p - view_; + return npos; + } + + friend BOOST_CXX14_CONSTEXPR bool operator==(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT { std::size_t idx = 0u; diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp index 9e62ab27a..489099c94 100644 --- a/include/boost/process/v2/detail/config.hpp +++ b/include/boost/process/v2/detail/config.hpp @@ -9,6 +9,8 @@ #include #include +#include +#include #if defined(ASIO_WINDOWS) #define BOOST_PROCESS_V2_WINDOWS 1 @@ -22,19 +24,29 @@ #define BOOST_PROCESS_V2_END_NAMESPACE } #else -#include +#include +#include #include #include #include -#if defined(BOOST_ASIO_WINDOWS) +#if defined(BOOST_WINDOWS_API) #define BOOST_PROCESS_V2_WINDOWS 1 #endif -#if defined(BOOST_ASIO_HAS_UNISTD_H) +#if defined(BOOST_POSIX_API) #define BOOST_PROCESS_V2_POSIX 1 #endif +#if defined(BOOST_PROCESS_USE_STD_FS) +#include +#include +#else +#include +#include +#include +#endif + #define BOOST_PROCESS_V2_BEGIN_NAMESPACE \ namespace boost { namespace process { namespace v2 { @@ -57,6 +69,9 @@ using std::error_code ; using std::error_category ; using std::system_category ; using std::system_error ; +namespace filesystem = std::filesystem; +using std::quoted; +using std::optional; #else @@ -64,6 +79,14 @@ using boost::system::error_code ; using boost::system::error_category ; using boost::system::system_category ; using boost::system::system_error ; +using boost::io::quoted; +using boost::optional; + +#ifdef BOOST_PROCESS_USE_STD_FS +namespace filesystem = std::filesystem; +#else +namespace filesystem = boost::filesystem; +#endif #endif diff --git a/include/boost/process/v2/detail/environment_posix.hpp b/include/boost/process/v2/detail/environment_posix.hpp new file mode 100644 index 000000000..23eb74420 --- /dev/null +++ b/include/boost/process/v2/detail/environment_posix.hpp @@ -0,0 +1,76 @@ +// +// process/environment/detail/environment_posix.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_DETAIL_ENVIRONMENT_POSIX_HPP +#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_POSIX_HPP + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace environment +{ + +using char_type = char; + +template +using key_char_traits = std::char_traits; + +template +using value_char_traits = std::char_traits; + + +constexpr char_type equality_sign = '='; +constexpr char_type delimiter = ':'; + +namespace detail +{ + +BOOST_PROCESS_V2_DECL +basic_cstring_ref> +get(basic_cstring_ref> key, error_code & ec); + +BOOST_PROCESS_V2_DECL +void set(basic_cstring_ref> key, + basic_cstring_ref> value, + error_code & ec); + +BOOST_PROCESS_V2_DECL +void unset(basic_cstring_ref> key, + error_code & ec); +} + + +using native_handle_type = const char * const *; +using native_iterator = native_handle_type; + +namespace detail +{ + +BOOST_PROCESS_V2_DECL native_handle_type load_native_handle(); +struct native_handle_deleter +{ + void operator()(native_handle_type) const {} +}; + +BOOST_PROCESS_V2_DECL native_iterator next(native_handle_type nh); +BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh); +inline const char_type * dereference(native_iterator iterator) {return *iterator;} + +BOOST_PROCESS_V2_DECL bool is_executable(const filesystem::path & pth, error_code & ec); + +} + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif \ No newline at end of file diff --git a/include/boost/process/v2/detail/environment_win.hpp b/include/boost/process/v2/detail/environment_win.hpp new file mode 100644 index 000000000..04cc18c9d --- /dev/null +++ b/include/boost/process/v2/detail/environment_win.hpp @@ -0,0 +1,194 @@ +// +// process/environment/detail/environment_win.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_DETAIL_ENVIRONMENT_WIN_HPP +#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace environment +{ + +using char_type = wchar_t; +template +struct key_char_traits +{ + typedef Char char_type; + typedef typename std::char_traits::int_type int_type; + typedef typename std::char_traits::off_type off_type; + typedef typename std::char_traits::pos_type pos_type; + typedef typename std::char_traits::state_type state_type; + + BOOST_CONSTEXPR static char to_upper(char c) {return std::toupper(to_int_type(c));} + BOOST_CONSTEXPR static wchar_t to_upper(wchar_t c) {return std::towupper(to_int_type(c));} + + BOOST_CONSTEXPR static int_type to_upper(int_type i, char ) {return std::toupper(i);} + BOOST_CONSTEXPR static int_type to_upper(int_type i, wchar_t) {return std::towupper(i);} + + + BOOST_CONSTEXPR static + void assign(char_type& c1, const char_type& c2) BOOST_NOEXCEPT + { + c1 = c2; + } + + BOOST_CONSTEXPR static + bool eq(char_type c1, char_type c2) BOOST_NOEXCEPT + { + return to_upper(c1) == to_upper(c2); + } + + BOOST_CONSTEXPR static + bool lt(char_type c1, char_type c2) BOOST_NOEXCEPT + { + return to_upper(c1) < to_upper(c2); + } + + BOOST_CONSTEXPR static + int compare(const char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT + { + auto itrs = std::mismatch(s1, s1 + n, s2, &eq); + if (itrs.first == (s1 + n)) + return 0; + auto c1 = to_upper(*itrs.first); + auto c2 = to_upper(*itrs.second); + + return (c1 < c2 ) ? -1 : 1; + } + + BOOST_CONSTEXPR static size_t length(const char* s) BOOST_NOEXCEPT { return std::strlen(s); } + BOOST_CONSTEXPR static size_t length(const wchar_t* s) BOOST_NOEXCEPT { return std::wcslen(s); } + + BOOST_CONSTEXPR static + const char_type* find(const char_type* s, size_t n, const char_type& a) BOOST_NOEXCEPT + { + const char_type u = to_upper(a); + return std::find_if(s, s + n, [u](char_type c){return to_upper(c) == u;}); + } + + BOOST_CONSTEXPR static + char_type* move(char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT + { + if (s1 < s2) + return std::move(s2, s2 + n, s1); + else + return std::move_backward(s2, s2 + n, s1); + } + + BOOST_CONSTEXPR static + char_type* copy(char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT + { + return std::copy(s2, s2 + n, s1); + } + + BOOST_CONSTEXPR static + char_type* assign(char_type* s, size_t n, char_type a) BOOST_NOEXCEPT + { + std::fill(s, s + n, a); + return s +n; + } + + BOOST_CONSTEXPR static + int_type not_eof(int_type c) BOOST_NOEXCEPT + { + return eq_int_type(c, eof()) ? ~eof() : c; + } + + BOOST_CONSTEXPR static + char_type to_char_type(int_type c) BOOST_NOEXCEPT + { + return char_type(c); + } + + BOOST_CONSTEXPR static + int_type to_int_type(char c) BOOST_NOEXCEPT + { + return int_type((unsigned char)c); + } + + BOOST_CONSTEXPR static + int_type to_int_type(wchar_t c) BOOST_NOEXCEPT + { + return int_type((wchar_t)c); + } + + BOOST_CONSTEXPR static + bool eq_int_type(int_type c1, int_type c2) BOOST_NOEXCEPT + { + return to_upper(c1, char_type()) == to_upper(c2, char_type()); + } + + BOOST_CONSTEXPR static inline int_type eof() BOOST_NOEXCEPT + { + return int_type(EOF); + } +}; + + +template +using value_char_traits = std::char_traits; + +BOOST_CONSTEXPR static char_type equality_sign = L'='; +BOOST_CONSTEXPR static char_type delimiter = L';'; + +using native_handle_type = wchar_t*; +using native_iterator = const wchar_t*; + +namespace detail +{ + +BOOST_PROCESS_V2_DECL +basic_cstring_ref> get( + basic_cstring_ref> key, + error_code & ec); + +BOOST_PROCESS_V2_DECL +void set(basic_cstring_ref> key, + basic_cstring_ref> value, + error_code & ec); + +BOOST_PROCESS_V2_DECL +void unset(basic_cstring_ref> key, + error_code & ec); + + +BOOST_PROCESS_V2_DECL +basic_cstring_ref> get( + basic_cstring_ref> key, + error_code & ec); + +BOOST_PROCESS_V2_DECL +void set(basic_cstring_ref> key, + basic_cstring_ref> value, + error_code & ec); + +BOOST_PROCESS_V2_DECL +void unset(basic_cstring_ref> key, + error_code & ec); + +inline native_handle_type load_native_handle(); +struct native_handle_deleter +{ + BOOST_PROCESS_V2_DECL void operator()(native_handle_type nh) const; + +}; + +inline const char_type * dereference(native_iterator iterator) {return iterator;} +BOOST_PROCESS_V2_DECL native_iterator next(native_handle_type nh); +BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh); +BOOST_PROCESS_V2_DECL bool is_executable(const filesystem::path & pth, error_code & ec); + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP diff --git a/include/boost/process/v2/detail/impl/environment.ipp b/include/boost/process/v2/detail/impl/environment.ipp new file mode 100644 index 000000000..7ef5d8fa9 --- /dev/null +++ b/include/boost/process/v2/detail/impl/environment.ipp @@ -0,0 +1,18 @@ +// 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_DETAIL_IMPL_ENVIRONMENT_HPP +#define BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_HPP + +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#elif defined(BOOST_PROCESS_V2_POSIX) +#include +#else +#error Operating System not supported. +#endif + +#endif //BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_HPP diff --git a/include/boost/process/v2/detail/impl/environment_posix.ipp b/include/boost/process/v2/detail/impl/environment_posix.ipp new file mode 100644 index 000000000..67c4f4229 --- /dev/null +++ b/include/boost/process/v2/detail/impl/environment_posix.ipp @@ -0,0 +1,81 @@ +// +// process/this_process/detail/environment_posix.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_DETAIL_ENVIRONMENT_WIN_HPP +#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP + +#include +#include +#include +#include + +#include +#include + + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace environment +{ +namespace detail +{ + +basic_cstring_ref> get( + basic_cstring_ref> key, + error_code & ec) +{ + auto res = ::getenv(key.c_str()); + if (res == nullptr) + { + ec = process::v2::detail::get_last_error(); + return {}; + } + return res; +} + +void set(basic_cstring_ref> key, + basic_cstring_ref> value, + error_code & ec) +{ + if (::setenv(key.c_str(), value.c_str(), true)) + ec = process::v2::detail::get_last_error(); +} + +void unset(basic_cstring_ref> key, error_code & ec) +{ + if (::unsetenv(key.c_str())) + ec = process::v2::detail::get_last_error(); +} + + +native_handle_type load_native_handle() { return ::environ; } + + +native_iterator next(native_iterator nh) +{ + return nh + 1; +} + +native_iterator find_end(native_handle_type nh) +{ + while (*nh != nullptr) + nh++; + return nh; +} +bool is_executable(const filesystem::path & p, error_code & ec) +{ + return filesystem::is_regular_file(p, ec) && (::access(p.c_str(), X_OK) == 0); +} + +} +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP diff --git a/include/boost/process/v2/detail/impl/environment_win.ipp b/include/boost/process/v2/detail/impl/environment_win.ipp new file mode 100644 index 000000000..54bf2c30f --- /dev/null +++ b/include/boost/process/v2/detail/impl/environment_win.ipp @@ -0,0 +1,147 @@ +// +// process/this_process/detail/environment_win.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_DETAIL_ENVIRONMENT_WIN_HPP +#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP + + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#include +#include +#include +#include + +#include "asio/cstring_view.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + + +namespace asio +{ +namespace environment +{ +namespace detail +{ + +std::basic_string> get( + ASIO_BASIC_CSTRING_VIEW_PARAM(char_type, key_char_traits) key, + error_code & ec) +{ + std::basic_string> buf; + + std::size_t size = 0u; + do + { + buf.resize(buf.size() + 4096); + size = ::GetEnvironmentVariableW(key.c_str(), buf.data(), buf.size()); + } + while (size == buf.size()); + + buf.resize(size); + + if (buf.size() == 0) + ec.assign(::GetLastError(), asio::error::get_system_category()); + + return buf; +} + +void set(ASIO_BASIC_CSTRING_VIEW_PARAM(char_type, key_char_traits) key, + ASIO_BASIC_CSTRING_VIEW_PARAM(char_type, value_char_traits) value, + error_code & ec) +{ + if (!::SetEnvironmentVariableW(key.c_str(), value.c_str())) + ec.assign(errno, asio::error::get_system_category()); +} + +void unset(ASIO_BASIC_CSTRING_VIEW_PARAM(char_type, key_char_traits) key, + error_code & ec) +{ + if (!::SetEnvironmentVariableW(key.c_str(), nullptr)) + ec.assign(errno, asio::error::get_system_category()); +} + + +std::basic_string> get( + ASIO_BASIC_CSTRING_VIEW_PARAM(char, key_char_traits) key, + error_code & ec) +{ + std::basic_string> buf; + + std::size_t size = 0u; + do + { + buf.resize(buf.size() + 4096); + size = ::GetEnvironmentVariableA(key.c_str(), buf.data(), buf.size()); + } + while (size == buf.size()); + + buf.resize(size); + + if (buf.size() == 0) + ec.assign(::GetLastError(), asio::error::get_system_category()); + + return buf; +} + +void set(ASIO_BASIC_CSTRING_VIEW_PARAM(char, key_char_traits) key, + ASIO_BASIC_CSTRING_VIEW_PARAM(char, value_char_traits) value, + error_code & ec) +{ + if (!::SetEnvironmentVariableA(key.c_str(), value.c_str())) + ec.assign(errno, asio::error::get_system_category()); +} + +void unset(ASIO_BASIC_CSTRING_VIEW_PARAM(char, key_char_traits) key, + error_code & ec) +{ + if (!::SetEnvironmentVariableA(key.c_str(), nullptr)) + ec.assign(errno, asio::error::get_system_category()); +} + + +native_handle_type load_native_handle() { return ::GetEnvironmentStringsW(); } +void native_handle_deleter::operator()(native_handle_type nh) const +{ + ::FreeEnvironmentStringsW(nh); +} + +native_iterator next(native_iterator nh) +{ + while (*nh != L'\0') + nh++; + return ++nh; +} + + +native_iterator find_end(native_handle_type nh) +{ + while ((*nh != L'\0') || (*std::next(nh) != L'\0')) + nh++; + return ++ ++nh; +} + +#if ASIO_HAS_FILESYSTEM +ASIO_DECL bool is_executable(const asio::filesystem::path & pth, error_code & ec) +{ + return asio::filesystem::is_regular_file(pth, ec) && SHGetFileInfoW(pth.native().c_str(), 0,0,0, SHGFI_EXETYPE); +} +#endif + +} +} +} + +#endif //BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp new file mode 100644 index 000000000..d410c30d2 --- /dev/null +++ b/include/boost/process/v2/environment.hpp @@ -0,0 +1,1455 @@ +// +// process/environment.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_ENVIRONMENT_HPP +#define BOOST_PROCESS_V2_ENVIRONMENT_HPP + +#include +#include +#include +#include +#include + +#if !defined(GENERATING_DOCUMENTATION) +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else +#include +#endif +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +/// Namespace for information and functions regarding the calling process. +namespace environment +{ + +#if defined(GENERATING_DOCUMENTATION) + +/// 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`. +*/ +tempalte +using key_char_traits = implementation-defined ; + +/// A char traits type that reflects the OS rules for string representing environment values. +/** Can be an alias of std::char_traits. May only be defined for `char` and `wchar_t`. +*/ +tempalte +using value_char_traits = implementation-defined ; + +/// The character type used by the environment. Either `char` or `wchar_t`. +using char_type = implementation-defined ; + +/// The equal character in an environment string used to separate key and value. +constexpr char_type equality_sign = implementation-defined; + +/// The delimiter in environemtn lists. Commonly used by the `PATH` variable. +constexpr char_type equality_sign = implementation-defined; + +/// The native handle of an environment. Note that this can be an owning pointer and is generally not thread safe. +using native_handle = implementation-defined; + + +#endif + + +/// The iterator used by a value or value_view to iterator through environments that are lists. +struct value_iterator +{ + using string_view_type = basic_string_view>; + using difference_type = std::size_t; + using value_type = string_view_type; + using pointer = const string_view_type *; + using reference = const string_view_type & ; + using iterator_category = std::forward_iterator_tag; + + value_iterator & operator++() + { + const auto delim = view_.find(delimiter); + if (delim != string_view_type::npos) + view_ = view_.substr(delim + 1); + else + view_ = view_.substr(view_.size()); + return *this; + } + + value_iterator operator++(int) + { + auto last = *this; + ++(*this); + return last; + } + string_view_type operator*() const + { + const auto delim = view_.find(delimiter); + if (delim == string_view_type::npos) + return view_; + else + return view_.substr(0, delim); + } + const string_view_type operator->() const + { + return **this; + } + + + value_iterator() = default; + value_iterator(const value_iterator & ) = default; + value_iterator(string_view_type view, std::size_t offset = 0u) : view_(view.substr(offset)) + { + } + + friend bool operator==(const value_iterator & l, const value_iterator & r) { return l.view_ == r.view_; } + friend bool operator!=(const value_iterator & l, const value_iterator & r) { return l.view_ != r.view_; } + friend bool operator<=(const value_iterator & l, const value_iterator & r) { return l.view_ <= r.view_; } + friend bool operator>=(const value_iterator & l, const value_iterator & r) { return l.view_ >= r.view_; } + friend bool operator< (const value_iterator & l, const value_iterator & r) { return l.view_ < r.view_; } + friend bool operator> (const value_iterator & l, const value_iterator & r) { return l.view_ > r.view_; } + + private: + string_view_type view_; +}; + +/// A view type for a key of an environment +struct key_view +{ + using value_type = char_type; + using traits_type = key_char_traits; + using string_view_type = basic_string_view; + using string_type = std::basic_string>; + + key_view() noexcept = default; + key_view( const key_view& p ) = default; + key_view( key_view&& p ) noexcept = default; + template::value>::type> + key_view( const Source& source ) : value_(source) {} + key_view( const char_type * p) : value_(p) {} + key_view( char_type * p) : value_(p) {} + + ~key_view() = default; + + key_view& operator=( const key_view& p ) = default; + key_view& operator=( key_view&& p ) noexcept = default; + key_view& operator=( string_view_type source ) + { + value_ = source; + return *this; + } + + void swap( key_view& other ) noexcept + { + std::swap(value_, other.value_); + } + + string_view_type native() const noexcept {return value_;} + + operator string_view_type() const {return native();} + + int compare( const key_view& p ) const noexcept {return value_.compare(p.value_);} + int compare( string_view_type str ) const {return value_.compare(str);} + int compare( const value_type* s ) const {return value_.compare(s);} + + template< class CharT, class Traits = std::char_traits, + class Alloc = std::allocator > + std::basic_string + string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale()) const + { + return boost::process::v2::detail::convert_chars(value_.data(), value_.data() + value_.size(), CharT(), alloc, loc); + } + + std::string string() const {return string();} + std::wstring wstring() const {return string();} + std::u16string u16string() const {return string();} + std::u32string u32string() const {return string();} + + string_type native_string() const + { + return string>(); + } + +#if BOOST_PROCESS_HAS_CHAR8_T + std::u8string u8string() const {return string();} +#endif + + friend bool operator==(key_view l, key_view r) { return l.value_ == r.value_; } + friend bool operator!=(key_view l, key_view r) { return l.value_ != r.value_; } + friend bool operator<=(key_view l, key_view r) { return l.value_ <= r.value_; } + friend bool operator>=(key_view l, key_view r) { return l.value_ >= r.value_; } + friend bool operator< (key_view l, key_view r) { return l.value_ < r.value_; } + friend bool operator> (key_view l, key_view r) { return l.value_ > r.value_; } + + bool empty() const {return value_.empty(); } + + template< class CharT, class Traits > + friend std::basic_ostream& + operator<<( std::basic_ostream& os, const key_view& p ) + { + os << boost::process::v2::quoted(p.string()); + return os; + } + + template< class CharT, class Traits > + friend std::basic_istream& + operator>>( std::basic_istream& is, key_view& p ) + { + std::basic_string t; + is >> boost::process::v2::quoted(t); + p = t; + return is; + } + const value_type * data() const {return value_.data(); } + std::size_t size() const {return value_.size(); } + private: + string_view_type value_; +}; + +struct value_view +{ + using value_type = char_type; + using string_view_type = basic_cstring_ref>; + using string_type = std::basic_string>; + using traits_type = value_char_traits; + + value_view() noexcept = default; + value_view( const value_view& p ) = default; + value_view( value_view&& p ) noexcept = default; + template::value>::type> + value_view( const Source& source ) : value_(source) {} + value_view( const char_type * p) : value_(p) {} + value_view( char_type * p) : value_(p) {} + + ~value_view() = default; + + value_view& operator=( const value_view& p ) = default; + value_view& operator=( value_view&& p ) noexcept = default; + value_view& operator=( string_view_type source ) + { + value_ = source; + return *this; + } + + void swap( value_view& other ) noexcept + { + std::swap(value_, other.value_); + } + + string_view_type native() const noexcept {return value_;} + + operator string_view_type() const {return native();} + operator typename string_view_type::string_view_type() const {return value_; } + + int compare( const value_view& p ) const noexcept {return value_.compare(p.value_);} + int compare( string_view_type str ) const {return value_.compare(str);} + int compare( const value_type* s ) const {return value_.compare(s);} + + template< class CharT, class Traits = std::char_traits, + class Alloc = std::allocator > + std::basic_string + string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale() ) const + { + return boost::process::v2::detail::convert_chars(value_.begin(), value_.end(), CharT(), alloc, loc); + } + + std::string string() const {return string();} + std::wstring wstring() const {return string();} + std::u16string u16string() const {return string();} + std::u32string u32string() const {return string();} + + string_type native_string() const + { + return string>(); + } + +#if BOOST_PROCESS_HAS_CHAR8_T + std::u8string u8string() const {return string();} +#endif + + bool empty() const {return value_.empty(); } + + friend bool operator==(value_view l, value_view r) { return l.value_ == r.value_; } + friend bool operator!=(value_view l, value_view r) { return l.value_ != r.value_; } + friend bool operator<=(value_view l, value_view r) { return l.value_ <= r.value_; } + friend bool operator>=(value_view l, value_view r) { return l.value_ >= r.value_; } + friend bool operator< (value_view l, value_view r) { return l.value_ < r.value_; } + friend bool operator> (value_view l, value_view r) { return l.value_ > r.value_; } + + + template< class CharT, class Traits > + friend std::basic_ostream& + operator<<( std::basic_ostream& os, const value_view& p ) + { + os << boost::process::v2::quoted(p.string()); + return os; + } + + template< class CharT, class Traits > + friend std::basic_istream& + operator>>( std::basic_istream& is, value_view& p ) + { + std::basic_string t; + is >> boost::process::v2::quoted(t); + p = t; + return is; + } + value_iterator begin() const {return value_iterator(value_.data());} + value_iterator end() const {return value_iterator(value_.data() , value_.size());} + + const char_type * c_str() {return value_.c_str(); } + const value_type * data() const {return value_.data(); } + std::size_t size() const {return value_.size(); } + + private: + string_view_type value_; +}; + +struct key_value_pair_view +{ + using value_type = char_type; + using string_type = std::basic_string; + using string_view_type = basic_cstring_ref; + using traits_type = std::char_traits; + + key_value_pair_view() noexcept = default; + key_value_pair_view( const key_value_pair_view& p ) = default; + key_value_pair_view( key_value_pair_view&& p ) noexcept = default; + template::value>::type> + key_value_pair_view( const Source& source ) : value_(source) {} + + key_value_pair_view( const char_type * p) : value_(p) {} + key_value_pair_view( char_type * p) : value_(p) {} + + + ~key_value_pair_view() = default; + + key_value_pair_view& operator=( const key_value_pair_view& p ) = default; + key_value_pair_view& operator=( key_value_pair_view&& p ) noexcept = default; + + void swap( key_value_pair_view& other ) noexcept + { + std::swap(value_, other.value_); + } + + string_view_type native() const noexcept {return value_;} + + operator string_view_type() const {return native();} + operator typename string_view_type::string_view_type() const {return value_; } + + int compare( const key_value_pair_view& p ) const noexcept {return value_.compare(p.value_);} + int compare( const string_type& str ) const {return value_.compare(str);} + int compare( string_view_type str ) const {return value_.compare(str);} + int compare( const value_type* s ) const {return value_.compare(s);} + + template< class CharT, class Traits = std::char_traits, class Alloc = std::allocator > + std::basic_string + string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale()) const + { + return boost::process::v2::detail::convert_chars(value_.begin(), value_.end(), CharT(), alloc, loc); + } + + std::string string() const {return string();} + std::wstring wstring() const {return string();} + std::u16string u16string() const {return string();} + std::u32string u32string() const {return string();} + + string_type native_string() const + { + return string(); + } + +#if BOOST_PROCESS_HAS_CHAR8_T + std::u8string u8string() const {return string();} +#endif + + bool empty() const {return value_.empty(); } + + key_view key() const + { + const auto eq = value_.find(equality_sign); + const auto res = native().substr(0, eq == string_view_type::npos ? value_.size() : eq); + return key_view::string_view_type(res.data(), res.size()); + } + value_view value() const + { + return environment::value_view(native().substr(value_.find(equality_sign) + 1)); + } + + friend bool operator==(key_value_pair_view l, key_value_pair_view r) { return l.value_ == r.value_; } + friend bool operator!=(key_value_pair_view l, key_value_pair_view r) { return l.value_ != r.value_; } + friend bool operator<=(key_value_pair_view l, key_value_pair_view r) { return l.value_ <= r.value_; } + friend bool operator>=(key_value_pair_view l, key_value_pair_view r) { return l.value_ >= r.value_; } + friend bool operator< (key_value_pair_view l, key_value_pair_view r) { return l.value_ < r.value_; } + friend bool operator> (key_value_pair_view l, key_value_pair_view r) { return l.value_ > r.value_; } + + + template< class CharT, class Traits > + friend std::basic_ostream& + operator<<( std::basic_ostream& os, const key_value_pair_view& p ) + { + os << boost::process::v2::quoted(p.string()); + return os; + } + + template< class CharT, class Traits > + friend std::basic_istream& + operator>>( std::basic_istream& is, key_value_pair_view& p ) + { + std::basic_string t; + is >> boost::process::v2::quoted(t); + p = t; + return is; + } + + template + inline auto get() const -> typename conditional::type; + const value_type * c_str() const noexcept + { + return value_.data(); + } + const value_type * data() const {return value_.data(); } + std::size_t size() const {return value_.size(); } + + private: + + string_view_type value_; +}; + +template<> +inline key_view key_value_pair_view::get<0u>() const +{ + return key(); +} + +template<> +inline value_view key_value_pair_view::get<1u>() const +{ + return value(); +} + +struct key +{ + using value_type = char_type; + using traits_type = key_char_traits; + using string_type = std::basic_string; + using string_view_type = basic_string_view; + + key() noexcept = default; + key( const key& p ) = default; + key( key&& p ) noexcept = default; + key( const string_type& source ) : value_(source) {} + key( string_type&& source ) : value_(std::move(source)) {} + key( const value_type * raw ) : value_(raw) {} + key( value_type * raw ) : value_(raw) {} + + explicit key(key_view kv) : value_(kv.string()) {} + + template< class Source > + key( const Source& source, const std::locale& loc = std::locale(), + decltype(source.data()) = nullptr) + : value_(boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc)) + { + } + + key(const typename conditional::value, wchar_t, char>::type * raw, const std::locale& loc = std::locale()) + : value_(boost::process::v2::detail::convert_chars( + raw, + raw + std::char_traits::type>::type>::length(raw), + char_type(), std::allocator(), loc)) + { + } + + key(const char16_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} + key(const char32_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} +#if BOOST_PROCESS_HAS_CHAR8_T + key(const char8_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} +#endif + + template + key(std::basic_string_view source, const std::locale& loc = std::locale()) + : value_(boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc)) + { + } + + template< class InputIt > + key( InputIt first, InputIt last, const std::locale& loc = std::locale()) + : key(std::basic_string(first, last), loc) + { + } + + ~key() = default; + + key& operator=( const key& p ) = default; + key& operator=( key&& p ) noexcept = default; + key& operator=( string_type&& source ) + { + value_ = std::move(source); + return *this; + } + template< class Source > + key& operator=( const Source& source ) + { + value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator()); + return *this; + } + + key& assign( string_type&& source ) + { + value_ = std::move(source); + return *this; + } + template< class Source > + key& assign( const Source& source , const std::locale & loc) + { + value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc); + return *this; + } + + template< class InputIt > + key& assign( InputIt first, InputIt last ) + { + return assign(std::string(first, last)); + } + + void clear() {value_.clear();} + + void swap( key& other ) noexcept + { + std::swap(value_, other.value_); + } + + const value_type* c_str() const noexcept {return value_.c_str();} + const string_type& native() const noexcept {return value_;} + string_view_type native_view() const noexcept {return value_;} + + operator string_type() const {return native();} + operator string_view_type() const {return native_view();} + + int compare( const key& p ) const noexcept {return value_.compare(p.value_);} + int compare( const string_type& str ) const {return value_.compare(str);} + int compare( string_view_type str ) const {return -str.compare(value_);} + int compare( const value_type* s ) const {return value_.compare(s);} + + template< class CharT, class Traits = std::char_traits, + class Alloc = std::allocator > + std::basic_string + string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale() ) const + { + return boost::process::v2::detail::convert_chars(value_.data(), value_.data() + value_.size(), CharT(), alloc, loc); + } + + std::string string() const {return string();} + std::wstring wstring() const {return string();} + std::u16string u16string() const {return string();} + std::u32string u32string() const {return string();} + + std::basic_string> native_string() const + { + return string>(); + } + +#if BOOST_PROCESS_HAS_CHAR8_T + std::u8string u8string() const {return string();} +#endif + + bool empty() const {return value_.empty(); } + + friend bool operator==(const key & l, const key & r) { return l.value_ == r.value_; } + friend bool operator!=(const key & l, const key & r) { return l.value_ != r.value_; } + friend bool operator<=(const key & l, const key & r) { return l.value_ <= r.value_; } + friend bool operator>=(const key & l, const key & r) { return l.value_ >= r.value_; } + friend bool operator< (const key & l, const key & r) { return l.value_ < r.value_; } + friend bool operator> (const key & l, const key & r) { return l.value_ > r.value_; } + + template< class CharT, class Traits > + friend std::basic_ostream& + operator<<( std::basic_ostream& os, const key& p ) + { + os << boost::process::v2::quoted(p.string()); + return os; + } + + template< class CharT, class Traits > + friend std::basic_istream& + operator>>( std::basic_istream& is, key& p ) + { + std::basic_string t; + is >> boost::process::v2::quoted(t); + p = t; + return is; + } + const value_type * data() const {return value_.data(); } + std::size_t size() const {return value_.size(); } + + private: + string_type value_; +}; + + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator==(const T &l, const U & r) { return key_view(l) == key_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator!=(const T &l, const U & r) { return key_view(l) != key_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator<=(const T &l, const U & r) { return key_view(l) <= key_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator <(const T &l, const U & r) { return key_view(l) < key_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator>=(const T &l, const U & r) { return key_view(l) >= key_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator >(const T &l, const U & r) { return key_view(l) > key_view(r); } + + +struct value +{ + using value_type = char_type; + using traits_type = value_char_traits; + using string_type = std::basic_string; + using string_view_type = basic_cstring_ref; + + value() noexcept = default; + value( const value& p ) = default; + + value( const string_type& source ) : value_(source) {} + value( string_type&& source ) : value_(std::move(source)) {} + value( const value_type * raw ) : value_(raw) {} + value( value_type * raw ) : value_(raw) {} + + explicit value(value_view kv) : value_(kv.c_str()) {} + + template< class Source > + value( const Source& source, const std::locale& loc = std::locale(), + decltype(source.data()) = nullptr) + : value_(boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc)) + { + } + + value(const typename conditional::value, wchar_t, char>::type * raw, const std::locale& loc = std::locale()) + : value_(boost::process::v2::detail::convert_chars( + raw, + raw + std::char_traits::type>::type>::length(raw), + char_type(), std::allocator(), loc)) + { + } + + value(const char16_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} + value(const char32_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} +#if BOOST_PROCESS_HAS_CHAR8_T + value(const char8_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} +#endif + + template< class InputIt > + value( InputIt first, InputIt last, const std::locale& loc = std::locale()) + : value(std::basic_string(first, last), loc) + { + } + + ~value() = default; + + value& operator=( const value& p ) = default; + value& operator=( value&& p ) noexcept = default; + value& operator=( string_type&& source ) + { + value_ = std::move(source); + return *this; + } + template< class Source > + value& operator=( const Source& source ) + { + value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator()); + return *this; + } + + value& assign( string_type&& source ) + { + value_ = std::move(source); + return *this; + } + template< class Source > + value& assign( const Source& source, const std::locale & loc = std::locale() ) + { + value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc); + return *this; + } + + template< class InputIt > + value& assign( InputIt first, InputIt last ) + { + return assign(std::string(first, last)); + } + + void push_back(const value & sv) + { + (value_ += delimiter) += sv; + } + + void clear() {value_.clear();} + + void swap( value& other ) noexcept + { + std::swap(value_, other.value_); + } + + const value_type* c_str() const noexcept {return value_.c_str();} + const string_type& native() const noexcept {return value_;} + string_view_type native_view() const noexcept {return value_;} + + operator string_type() const {return native();} + operator string_view_type() const {return native_view();} + operator typename string_view_type::string_view_type() const {return value_; } + + int compare( const value& p ) const noexcept {return value_.compare(p.value_);} + int compare( const string_type& str ) const {return value_.compare(str);} + int compare( string_view_type str ) const {return -str.compare(value_);} + int compare( const value_type* s ) const {return value_.compare(s);} + + template< class CharT, class Traits = std::char_traits, + class Alloc = std::allocator > + std::basic_string + string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale()) const + { + return boost::process::v2::detail::convert_chars(value_.data(), value_.data() + value_.size(), CharT(), alloc, loc); + } + + std::string string() const {return string();} + std::wstring wstring() const {return string();} + std::u16string u16string() const {return string();} + std::u32string u32string() const {return string();} + + std::basic_string> native_string() const + { + return string>(); + } + +#if BOOST_PROCESS_HAS_CHAR8_T + std::u8string u8string() const {return string();} +#endif + + bool empty() const {return value_.empty(); } + + friend bool operator==(const value & l, const value & r) { return l.value_ == r.value_; } + friend bool operator!=(const value & l, const value & r) { return l.value_ != r.value_; } + friend bool operator<=(const value & l, const value & r) { return l.value_ <= r.value_; } + friend bool operator>=(const value & l, const value & r) { return l.value_ >= r.value_; } + friend bool operator< (const value & l, const value & r) { return l.value_ < r.value_; } + friend bool operator> (const value & l, const value & r) { return l.value_ > r.value_; } + + template< class CharT, class Traits > + friend std::basic_ostream& + operator<<( std::basic_ostream& os, const value& p ) + { + os << boost::process::v2::quoted(p.string()); + return os; + } + + template< class CharT, class Traits > + friend std::basic_istream& + operator>>( std::basic_istream& is, value& p ) + { + std::basic_string t; + is >> boost::process::v2::quoted(t); + p = t; + return is; + } + + value_iterator begin() const {return value_iterator(value_.data());} + value_iterator end() const {return value_iterator(value_.data(), value_.size());} + const value_type * data() const {return value_.data(); } + std::size_t size() const {return value_.size(); } + + private: + string_type value_; +}; + + + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator==(const T &l, const U & r) { return value_view(l) == value_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator!=(const T &l, const U & r) { return value_view(l) != value_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator<=(const T &l, const U & r) { return value_view(l) <= value_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator <(const T &l, const U & r) { return value_view(l) < value_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator>=(const T &l, const U & r) { return value_view(l) >= value_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator >(const T &l, const U & r) { return value_view(l) > value_view(r); } + + + + +struct key_value_pair +{ + using value_type = char_type; + using traits_type = std::char_traits; + using string_type = std::basic_string; + using string_view_type = basic_cstring_ref; + + key_value_pair() noexcept = default; + key_value_pair( const key_value_pair& p ) = default; + key_value_pair( key_value_pair&& p ) noexcept = default; + key_value_pair(key_view key, value_view value) : value_(key.string() + equality_sign + value.string()) {} + + key_value_pair(key_view key, std::initializer_list> values) + { + const auto sz = std::accumulate(values.begin(), values.end(), + key.size(), [](std::size_t sz, const auto & str) { return sz + str.size() + 1;}); + + value_.reserve(sz); + value_.append(key.data(), key.size()); + value_ += equality_sign; + for (auto & value : values) + { + if (value_.back() != equality_sign) + value_ += delimiter; + value_.append(value.data(), value.size()); + } + } + + key_value_pair( const string_type& source ) : value_(source) {} + key_value_pair( string_type&& source ) : value_(std::move(source)) {} + key_value_pair( const value_type * raw ) : value_(raw) {} + key_value_pair( value_type * raw ) : value_(raw) {} + + explicit key_value_pair(key_value_pair_view kv) : value_(kv.c_str()) {} + + template< class Source > + key_value_pair( const Source& source, const std::locale& loc = std::locale(), + decltype(source.data()) = nullptr) + : value_(boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc)) + { + } + + key_value_pair(const typename conditional::value, wchar_t, char>::type * raw, const std::locale& loc = std::locale()) + : value_(boost::process::v2::detail::convert_chars( + raw, + raw + std::char_traits::type>::type>::length(raw), + char_type(), std::allocator(), loc)) + { + } + + key_value_pair(const char16_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} + key_value_pair(const char32_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} +#if BOOST_PROCESS_HAS_CHAR8_T + key_value_pair(const char8_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} +#endif + + template< class InputIt , typename std::iterator_traits::iterator_category> + key_value_pair( InputIt first, InputIt last, const std::locale& loc = std::locale()) + : key_value_pair(std::basic_string(first, last), loc) + { + } + + ~key_value_pair() = default; + + key_value_pair& operator=( const key_value_pair& p ) = default; + key_value_pair& operator=( key_value_pair&& p ) noexcept = default; + key_value_pair& operator=( string_type&& source ) + { + value_ = std::move(source); + return *this; + } + template< class Source > + key_value_pair& operator=( const Source& source ) + { + value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator()); + return *this; + } + + key_value_pair& assign( string_type&& source ) + { + value_ = std::move(source); + return *this; + } + template< class Source > + key_value_pair& assign( const Source& source, const std::locale & loc = std::locale() ) + { + value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc); + return *this; + } + + template< class InputIt > + key_value_pair& assign( InputIt first, InputIt last ) + { + return assign(std::string(first, last)); + } + + void clear() {value_.clear();} + + void swap( key_value_pair& other ) noexcept + { + std::swap(value_, other.value_); + } + + const value_type* c_str() const noexcept {return value_.c_str();} + const string_type& native() const noexcept {return value_;} + string_view_type native_view() const noexcept {return value_;} + + operator string_type() const {return native();} + operator string_view_type() const {return native_view();} + + int compare( const key_value_pair& p ) const noexcept {return value_.compare(p.value_);} + int compare( const string_type& str ) const {return value_.compare(str);} + int compare( string_view_type str ) const {return -str.compare(value_);} + int compare( const value_type* s ) const {return value_.compare(s);} + + template< class CharT, class Traits = std::char_traits, class Alloc = std::allocator > + std::basic_string + string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale() ) const + { + return boost::process::v2::detail::convert_chars(value_.data(), value_.data() + value_.size(), CharT(), alloc, loc); + } + + std::string string() const {return string();} + std::wstring wstring() const {return string();} + std::u16string u16string() const {return string();} + std::u32string u32string() const {return string();} + + std::basic_string> native_string() const + { + return string>(); + } + + friend bool operator==(const key_value_pair & l, const key_value_pair & r) { return l.value_ == r.value_; } + friend bool operator!=(const key_value_pair & l, const key_value_pair & r) { return l.value_ != r.value_; } + friend bool operator<=(const key_value_pair & l, const key_value_pair & r) { return l.value_ <= r.value_; } + friend bool operator>=(const key_value_pair & l, const key_value_pair & r) { return l.value_ >= r.value_; } + friend bool operator< (const key_value_pair & l, const key_value_pair & r) { return l.value_ < r.value_; } + friend bool operator> (const key_value_pair & l, const key_value_pair & r) { return l.value_ > r.value_; } + +#if BOOST_PROCESS_HAS_CHAR8_T + std::u8string u8string() const {return string();} +#endif + + bool empty() const {return value_.empty(); } + + struct key_view key() const + { + const auto k = native_view().substr(0, value_.find(equality_sign)); + return boost::process::v2::environment::key_view::string_view_type (k.data(), k.size()); + } + struct value_view value() const + { + return value_view::string_view_type(native_view().substr(value_.find(equality_sign) + 1)); + } + + template< class CharT, class Traits > + friend std::basic_ostream& + operator<<( std::basic_ostream& os, const key_value_pair& p ) + { + os << boost::process::v2::quoted(p.string()); + return os; + } + + template< class CharT, class Traits > + friend std::basic_istream& + operator>>( std::basic_istream& is, key_value_pair& p ) + { + is >> boost::process::v2::quoted(p.value_); + return is; + } + + template + auto get() const + { + if constexpr (Idx == 0u) + return key_view(); + else + return value_view(); + } + + template + inline auto get() const -> typename conditional::type; + + const value_type * data() const {return value_.data(); } + std::size_t size() const {return value_.size(); } + +private: + + string_type value_; +}; + + + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator==(const T &l, const U & r) { return key_value_pair_view(l) == key_value_pair_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator!=(const T &l, const U & r) { return key_value_pair_view(l) != key_value_pair_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator<=(const T &l, const U & r) { return key_value_pair_view(l) <= key_value_pair_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator <(const T &l, const U & r) { return key_value_pair_view(l) < key_value_pair_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator>=(const T &l, const U & r) { return key_value_pair_view(l) >= key_value_pair_view(r); } + +template +typename std::enable_if< + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value) + || + ((std::is_same::value || std::is_same::value) && + std::is_convertible::value), + bool>::type +operator >(const T &l, const U & r) { return key_value_pair_view(l) > key_value_pair_view(r); } + + +template<> +inline key_view key_value_pair::get<0u>() const +{ + return key(); +} + +template<> +inline value_view key_value_pair::get<1u>() const +{ + return value(); +} + +struct view +{ + using native_handle_type = environment::native_handle_type; + using value_type = key_value_pair_view; + + view() = default; + view(view && nt) = default; + + native_handle_type native_handle() { return handle_.get(); } + + struct iterator + { + using value_type = key_value_pair_view; + using iterator_category = std::forward_iterator_tag; + + iterator() = default; + iterator(const iterator & ) = default; + iterator(const native_iterator &native_handle) : iterator_(native_handle) {} + + iterator & operator++() + { + iterator_ = detail::next(iterator_); + return *this; + } + + iterator operator++(int) + { + auto last = *this; + iterator_ = detail::next(iterator_); + return last; + } + + + const key_value_pair_view operator*() const + { + return key_value_pair_view(detail::dereference(iterator_)); + } + + optional operator->() const + { + return key_value_pair_view(detail::dereference(iterator_)); + } + + friend bool operator==(const iterator & l, const iterator & r) {return l.iterator_ == r.iterator_;} + friend bool operator!=(const iterator & l, const iterator & r) {return l.iterator_ != r.iterator_;} + + private: + environment::native_iterator iterator_; + }; + + iterator begin() const {return iterator(handle_.get());} + iterator end() const {return iterator(detail::find_end(handle_.get()));} + + private: + + std::unique_ptr::type, + detail::native_handle_deleter> handle_{environment::detail::load_native_handle()}; +}; + + +template +inline boost::process::v2::filesystem::path home(Environment && env = view()) +{ + auto find_key = [&](key_view ky) -> value + { + const auto itr = std::find_if(std::begin(env), std::end(env), + [&](key_value_pair vp) + { + auto tmp = vp.key() == ky; + if (tmp) + return true; + else + return false; + }); + if (itr != nullptr) + return itr->value_view(); + else + return value_view(); + }; +#if defined(ASIO_WINDOWS) + return find_key(L"HOMEDRIVE") + find_key(L"HOMEPATH"); +#else + return find_key(L"HOME"); +#endif + +} + +template +inline boost::process::v2::filesystem::path find_executable( + boost::process::v2::filesystem::path name, + Environment && env = view()) +{ + auto find_key = [&](key_view ky) -> value_view + { + const auto itr = std::find_if(std::begin(env), std::end(env), + [&](key_value_pair vp) + { + auto tmp = vp.key() == ky; + if (tmp) + return true; + else + return false; + }); + if (itr != nullptr) + return itr->value_view(); + else + return value_view(); + }; + +#if defined(BOOST_PROCESS_V2_WINDOWS) + auto path = find_key(L"PATH"); + auto pathext = find_key(L"PATHEXT"); + for (auto pp_view : path) + for (auto ext : pathext) + { + boost::process::v2::filesystem::path nm(name); + nm += ext; + + auto p = boost::process::v2::filesystem::path(pp_view) / nm; + + error_code ec; + bool file = boost::process::v2::filesystem::is_regular_file(p, ec); + if (!ec && file && SHGetFileInfoW(p.native().c_str(), 0,0,0, SHGFI_EXETYPE)) + return p; + } +#else + auto path = find_key("PATH"); + for (auto pp_view : path) + { + auto p = boost::process::v2::filesystem::path(pp_view) / name; + error_code ec; + bool file = boost::process::v2::filesystem::is_regular_file(p, ec); + if (!ec && file && ::access(p.c_str(), X_OK) == 0) + return p; + } +#endif + return {}; +} + + +inline value get(const key & k, error_code & ec) { return detail::get(k.c_str(), ec);} +inline value get(const key & k) +{ + error_code ec; + auto tmp = detail::get(k.c_str(), ec); + boost::process::v2::detail::throw_error(ec, "environment::get"); + return tmp; +} + +inline value get(basic_cstring_ref> k, error_code & ec) +{ + return detail::get(k, ec); +} +inline value get(basic_cstring_ref> k) +{ + error_code ec; + auto tmp = detail::get(k, ec); + boost::process::v2::detail::throw_error(ec, "environment::get"); + return tmp; +} + + +inline value get(const char_type * c, error_code & ec) { return detail::get(c, ec);} +inline value get(const char_type * c) +{ + error_code ec; + auto tmp = detail::get(c, ec); + boost::process::v2::detail::throw_error(ec, "environment::get"); + return tmp; +} + +inline void set(const key & k, value_view vw, error_code & ec) { detail::set(k, vw, ec);} +inline void set(const key & k, value_view vw) +{ + error_code ec; + detail::set(k, vw, ec); + boost::process::v2::detail::throw_error(ec, "environment::set"); +} + +inline void set(basic_cstring_ref> k, value_view vw, error_code & ec) { detail::set(k, vw, ec);} +inline void set(basic_cstring_ref> k, value_view vw) +{ + error_code ec; + detail::set(k, vw, ec); + boost::process::v2::detail::throw_error(ec, "environment::set"); +} + + +inline void set(const char_type * k, value_view vw, error_code & ec) { detail::set(k, vw, ec);} +inline void set(const char_type * k, value_view vw) +{ + error_code ec; + detail::set(k, vw, ec); + boost::process::v2::detail::throw_error(ec, "environment::set"); +} + +template::value>::type> +inline void set(const key & k, const Char * vw, error_code & ec) +{ + value val{vw}; + detail::set(k, val, ec); +} +template::value>::type> +inline void set(const key & k, const Char * vw) +{ + error_code ec; + value val{vw}; + detail::set(k, val, ec); + boost::process::v2::detail::throw_error(ec, "environment::set"); +} + +template::value>::type> +inline void set(basic_cstring_ref> k, const Char * vw, error_code & ec) +{ + value val{vw}; + detail::set(k, val, ec); +} +template::value>::type> +inline void set(basic_cstring_ref> k, const Char * vw) +{ + error_code ec; + value val{vw}; + detail::set(k, val, ec); + boost::process::v2::detail::throw_error(ec, "environment::set"); +} + + +template::value>::type> +inline void set(const char_type * k, const Char * vw, error_code & ec) +{ + value val{vw}; + detail::set(k, val, ec); +} +template::value>::type> +inline void set(const char_type * k, const Char * vw) +{ + error_code ec; + value val{vw}; + detail::set(k, val, ec); + boost::process::v2::detail::throw_error(ec, "environment::set"); +} + + + +inline void unset(const key & k, error_code & ec) { detail::unset(k, ec);} +inline void unset(const key & k) +{ + error_code ec; + detail::unset(k, ec); + boost::process::v2::detail::throw_error(ec, "environment::unset"); +} + +inline void unset(basic_cstring_ref> k, error_code & ec) +{ + detail::unset(k, ec); +} +inline void unset(basic_cstring_ref> k) +{ + error_code ec; + detail::unset(k, ec); + boost::process::v2::detail::throw_error(ec, "environment::unset"); +} + + +inline void unset(const char_type * c, error_code & ec) { detail::unset(c, ec);} +inline void unset(const char_type * c) +{ + error_code ec; + detail::unset(c, ec); + boost::process::v2::detail::throw_error(ec, "environment::unset"); +} +} + +BOOST_PROCESS_V2_END_NAMESPACE + +namespace std +{ + +template<> +struct tuple_size : integral_constant {}; + +template<> +struct tuple_element<0u, boost::process::v2::environment::key_value_pair> {using type = boost::process::v2::environment::key_view;}; + +template<> +struct tuple_element<1u, boost::process::v2::environment::key_value_pair> {using type = boost::process::v2::environment::value_view;}; + +template<> +struct tuple_size : integral_constant {}; + +template<> +struct tuple_element<0u, boost::process::v2::environment::key_value_pair_view> +{ + using type = boost::process::v2::environment::key_view; +}; + +template<> +struct tuple_element<1u, boost::process::v2::environment::key_value_pair_view> +{ + using type = boost::process::v2::environment::value_view; +}; + + +} + + +#endif //BOOST_PROCESS_V2_ENVIRONMENT_HPP diff --git a/include/boost/process/v2/src.hpp b/include/boost/process/v2/src.hpp index 01c3fd0c9..2beecc2c1 100644 --- a/include/boost/process/v2/src.hpp +++ b/include/boost/process/v2/src.hpp @@ -15,8 +15,9 @@ #include #include +#include +#include #include #include -#include #endif //BOOST_PROCESS_V2_SRC_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9b926196f..7867c8adf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -71,4 +71,6 @@ process_sparring_partner_launch(on_exit) process_sparring_partner_launch(on_exit2) process_sparring_partner_launch(on_exit3) process_sparring_partner_launch(posix_specific) -process_sparring_partner_launch(windows_specific) \ No newline at end of file +process_sparring_partner_launch(windows_specific) + +add_subdirectory(v2) \ No newline at end of file diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index 9839fd80f..114b14796 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -1,18 +1,17 @@ +enable_testing() add_library(boost_process_v2_test_impl test_impl.cpp) target_link_libraries(boost_process_v2_test_impl Boost::unit_test_framework Boost::process) target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1) target_include_directories(boost_process_v2_test_impl PUBLIC ../../include) -enable_testing() - -add_executable(boost_process_v2_codecvt codecvt.cpp) -target_link_libraries(boost_process_v2_codecvt boost_process_v2_test_impl) -add_test(NAME boost_process_v2_codecvt COMMAND $) -add_executable(boost_process_v2_cstring_ref cstring_ref.cpp) -target_link_libraries(boost_process_v2_cstring_ref boost_process_v2_test_impl) -add_test(NAME boost_process_v2_cstring_ref COMMAND $) +function(boost_process_v2_standalone_test name) + add_executable(boost_process_v2_${name} ${name}.cpp) + target_link_libraries(boost_process_v2_${name} Boost::system Boost::filesystem boost_process_v2_test_impl) + add_test(NAME boost_process_v2_${name} COMMAND $ ) +endfunction() -add_executable(boost_process_v2_pid pid.cpp) -target_link_libraries(boost_process_v2_pid boost_process_v2_test_impl) -add_test(NAME boost_process_v2_pid COMMAND $) \ No newline at end of file +boost_process_v2_standalone_test(codecvt) +boost_process_v2_standalone_test(cstring_ref) +boost_process_v2_standalone_test(pid) +boost_process_v2_standalone_test(environment) \ No newline at end of file diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp new file mode 100644 index 000000000..637a4f8d4 --- /dev/null +++ b/test/v2/environment.cpp @@ -0,0 +1,116 @@ +// 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) + + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include + +#include + +namespace bp2 = boost::process::v2; +namespace bpe = boost::process::v2::environment; + + +BOOST_AUTO_TEST_CASE(environment) +{ + BOOST_CHECK_EQUAL(bp2::filesystem::current_path(), bpe::get("PWD")); + + for (const auto & elem : bpe::get("PATH")) + BOOST_CHECK(std::find(elem.begin(), elem.end(), bpe::delimiter) == elem.end()); + + BOOST_CHECK(bpe::get("PATH").size() > 0); + + const auto key1 = "BP2_TEST_NAME"; +#if defined(BOOST_PROCESS_V2_WINDOWS) + const auto key2 = "BP2_TeSt_NamE"; +#else + const auto key2 = key1; +#endif + + BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); + bp2::error_code ec; + bpe::get(key2, ec); + BOOST_CHECK_EQUAL(ec, boost::system::errc::permission_denied); + + bpe::set(key1, "some test string"); + BOOST_CHECK(bpe::get(key1) == "some test string"); + BOOST_CHECK(bpe::get(key2) == "some test string"); + bpe::unset(key2); + + BOOST_CHECK_THROW(bpe::set("invalid=", "blablubb") , bp2::system_error); + BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); + bpe::get(key2, ec); + BOOST_CHECK_EQUAL(ec, boost::system::errc::invalid_argument); + + for (const auto ke : bpe::view()) + BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); + +#if defined(BOOST_PROCESS_V2_POSIX) + BOOST_CHECK_EQUAL(bpe::key("FOO"), bpe::key_view("FOO")); + BOOST_CHECK_EQUAL(bpe::key("FOO"), std::string("FOO")); + BOOST_CHECK_EQUAL(bpe::key_value_pair("FOO=BAR"), bpe::key_value_pair_view("FOO=BAR")); + BOOST_CHECK_EQUAL(bpe::key_value_pair("FOO", "BAR"), bpe::key_value_pair_view("FOO=BAR")); + + using sv = bpe::value::string_type; + std::string cmp = sv("FOO=X") + bpe::delimiter + sv("YY") + bpe::delimiter + sv("Z42"); + BOOST_CHECK_EQUAL(bpe::key_value_pair("FOO", {"X", "YY", "Z42"}), cmp); +#endif + +} + + + +BOOST_AUTO_TEST_CASE(wenvironment) +{ + BOOST_CHECK_EQUAL(bp2::filesystem::current_path(), bpe::get(L"PWD")); + + for (const auto & elem : bpe::get(L"PATH")) + BOOST_CHECK(std::find(elem.begin(), elem.end(), bpe::delimiter) == elem.end()); + + BOOST_CHECK(bpe::get(L"PATH").size() > 0); + + const auto key1 = L"BP2_TEST_NAME"; +#if defined(BOOST_PROCESS_V2_WINDOWS) + const auto key2 = L"BP2_TeSt_NamE"; +#else + const auto key2 = key1; +#endif + + BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); + bp2::error_code ec; + bpe::get(key2, ec); + BOOST_CHECK_EQUAL(ec, boost::system::errc::permission_denied); + + bpe::set(key1, L"some test string"); + BOOST_CHECK(bpe::get(key1) == L"some test string"); + BOOST_CHECK(bpe::get(key2) == L"some test string"); + bpe::unset(key2); + + BOOST_CHECK_THROW(bpe::set(L"invalid=", L"blablubb") , bp2::system_error); + BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); + bpe::get(key2, ec); + BOOST_CHECK_EQUAL(ec, boost::system::errc::invalid_argument); + + for (const auto ke : bpe::view()) + BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); + +#if defined(BOOST_PROCESS_V2_WINDOWS) + BOOST_CHECK_EQUAL(bpe::key(L"FOO"), bpe::key_view(L"FOO")); + BOOST_CHECK_EQUAL(bpe::key(L"FOO"), std::wstring(L"FOO")); + BOOST_CHECK_EQUAL(bpe::key_value_pair(L"FOO=BAR"), bpe::key_value_pair_view(L"FOO=BAR")); + BOOST_CHECK_EQUAL(bpe::key_value_pair(L"FOO", L"BAR"), bpe::key_value_pair_view(L"FOO=BAR")); + + using sv = bpe::value::string_type; + std::string cmp = sv(L"FOO=X") + bpe::delimiter + sv(L"YY") + bpe::delimiter + sv(L"Z42"); + BOOST_CHECK_EQUAL(bpe::key_value_pair(L"FOO", {L"X", L"YY", L"Z42"}), cmp); + +#endif +} + From c473251709a6b9509331c0f67786a5cb888c0307 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 20 May 2022 12:25:32 +0800 Subject: [PATCH 050/397] Added windows environment stuff. --- CMakeLists.txt | 1 - include/boost/process/v2/cstring_ref.hpp | 4 +- include/boost/process/v2/detail/codecvt.hpp | 91 +++++++++---------- include/boost/process/v2/detail/config.hpp | 17 +++- .../process/v2/detail/environment_win.hpp | 8 +- .../boost/process/v2/detail/impl/codecvt.ipp | 15 ++- .../process/v2/detail/impl/environment.ipp | 6 +- .../v2/detail/impl/environment_posix.ipp | 6 +- .../v2/detail/impl/environment_win.ipp | 65 ++++++------- .../boost/process/v2/detail/last_error.hpp | 1 + include/boost/process/v2/impl/error.ipp | 2 +- include/boost/process/v2/pid.hpp | 2 - test/v2/Jamfile.jam | 48 ++++++++++ test/v2/codecvt.cpp | 4 +- test/v2/cstring_ref.cpp | 2 +- 15 files changed, 167 insertions(+), 105 deletions(-) create mode 100644 test/v2/Jamfile.jam diff --git a/CMakeLists.txt b/CMakeLists.txt index 20e7499fe..cb2db9110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,6 @@ cmake_minimum_required(VERSION 3.5...3.16) project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) -find_package(Boost REQUIRED iostreams program_options filesystem system thread) add_library(boost_process INTERFACE) add_library(Boost::process ALIAS boost_process) diff --git a/include/boost/process/v2/cstring_ref.hpp b/include/boost/process/v2/cstring_ref.hpp index b45545d60..7cfcfd7ef 100644 --- a/include/boost/process/v2/cstring_ref.hpp +++ b/include/boost/process/v2/cstring_ref.hpp @@ -63,7 +63,7 @@ struct basic_cstring_ref >::value>::type> BOOST_CONSTEXPR basic_cstring_ref(Source && src) : view_(src.c_str()) {} - BOOST_CONSTEXPR typename std::basic_string_view::const_pointer c_str() const BOOST_NOEXCEPT + BOOST_CONSTEXPR typename basic_string_view::const_pointer c_str() const BOOST_NOEXCEPT { return this->data(); } @@ -95,7 +95,7 @@ struct basic_cstring_ref BOOST_CONSTEXPR size_type size() const BOOST_NOEXCEPT {return length(); } BOOST_CONSTEXPR size_type length() const BOOST_NOEXCEPT {return traits_type::length(view_); } - BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT {return std::numeric_limits::max() / sizeof(CharT); } + BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT {return (std::numeric_limits::max)() / sizeof(CharT); } BOOST_ATTRIBUTE_NODISCARD BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT {return *view_ == *detail::null_char_(CharT{}); } BOOST_CONSTEXPR const_reference operator[](size_type pos) const {return view_[pos] ;} diff --git a/include/boost/process/v2/detail/codecvt.hpp b/include/boost/process/v2/detail/codecvt.hpp index 81561397f..935c275c8 100644 --- a/include/boost/process/v2/detail/codecvt.hpp +++ b/include/boost/process/v2/detail/codecvt.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Klemens D. Morgenstern +// 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) @@ -19,7 +19,7 @@ namespace detail #if defined(BOOST_PROCESS_V2_WINDOWS) -BOOST_PROCESS_V2_DECL const std::codecvt< wchar_t, char, std::mbstate_t > & default_locale(); +BOOST_PROCESS_V2_DECL const std::codecvt< wchar_t, char, std::mbstate_t > & default_codecvt(); #else @@ -83,10 +83,10 @@ inline std::basic_string convert_chars( std::basic_string tmp(len, ' ', alloc); auto itr = begin; - auto out_itr = tmp.data(); - auto e = f.out(mb, begin, end, itr, tmp.data(), tmp.data() + tmp.size(), out_itr); + auto out_itr = &tmp.front(); + auto e = f.out(mb, begin, end, itr, &tmp.front(), &tmp.back() + 1, out_itr); ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - tmp.data()); + tmp.resize(out_itr - &tmp.front()); return tmp; } @@ -108,14 +108,14 @@ inline std::basic_string convert_chars( : std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = f.length(mb, begin, end, std::numeric_limits::max()); + const std::size_t len = f.length(mb, begin, end, (std::numeric_limits::max)()); std::basic_string res(len, L' ', alloc); auto itr = begin; - auto out_itr = res.data(); - auto e = f.in(mb, begin, end, itr, res.data(), res.data() + res.size(), out_itr); + auto out_itr = &res.front(); + auto e = f.in(mb, begin, end, itr, &res.front(), &res.back() + 1, out_itr); ec.assign(e, error::get_codecvt_category()); - res.resize(out_itr - res.data()); + res.resize(out_itr - &res.front()); return res; } @@ -184,13 +184,13 @@ inline std::basic_string convert_chars( std::basic_string tmp(len, ' ', alloc); auto itr = begin; - auto out_itr = tmp.data(); + auto out_itr = &tmp.front(); auto e = f.out(mb, begin, end, itr, - reinterpret_cast(tmp.data()), - reinterpret_cast(tmp.data() + tmp.size()), + reinterpret_cast(&tmp.front()), + reinterpret_cast(&tmp.back()), reinterpret_cast(out_itr)); ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - tmp.data()); + tmp.resize(out_itr - &tmp.front()); return tmp; } @@ -214,14 +214,14 @@ inline std::basic_string convert_chars( std::basic_string tmp(len, ' ', alloc); auto itr = begin; - auto out_itr = tmp.data(); + auto out_itr = &tmp.front(); auto e = f.out(mb, begin, end, itr, - reinterpret_cast(tmp.data()), - reinterpret_cast(tmp.data() + tmp.size()), + reinterpret_cast(&tmp.front()), + reinterpret_cast(&tmp.back()), reinterpret_cast(out_itr)); ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - tmp.data()); + tmp.resize(out_itr - &tmp.front()); return tmp; } @@ -245,14 +245,14 @@ inline std::basic_string convert_chars( std::basic_string tmp(len, ' ', alloc); auto itr = begin; - auto out_itr = tmp.data(); + auto out_itr = &tmp.front(); auto e = f.out(mb, begin, end, itr, - reinterpret_cast(tmp.data()), - reinterpret_cast(tmp.data() + tmp.size()), + reinterpret_cast(&tmp.front()), + reinterpret_cast(&tmp.back()), reinterpret_cast(out_itr)); ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - tmp.data()); + tmp.resize(out_itr - &tmp.front()); return tmp; } @@ -279,14 +279,14 @@ inline std::basic_string convert_chars( std::basic_string res(len, u' ', alloc); auto itr = begin; - auto out_itr = res.data(); + auto out_itr = &res.front(); auto e = f.in(mb, reinterpret_cast(begin), reinterpret_cast(end), reinterpret_cast(itr), - res.data(), res.data() + res.size(), out_itr); + &res.front(), &res.back() + 1, out_itr); ec.assign(e, error::get_codecvt_category()); - res.resize(out_itr - res.data()); + res.resize(out_itr - &res.front()); return res; } @@ -313,15 +313,15 @@ inline std::basic_string convert_chars( std::basic_string res(len, U' ', alloc); auto itr = begin; - auto out_itr = res.data(); + auto out_itr = &res.front(); auto e = f.in(mb, reinterpret_cast(begin), reinterpret_cast(end), reinterpret_cast(itr), - res.data(), res.data() + res.size(), out_itr); + &res.front(), &res.back() + 1, out_itr); ec.assign(e, error::get_codecvt_category()); - res.resize(out_itr - res.data()); + res.resize(out_itr - &res.front()); return res; } @@ -347,11 +347,11 @@ inline std::basic_string convert_chars( std::basic_string tmp(len, ' ', alloc); auto itr = begin; - auto out_itr = tmp.data(); - auto e = f.out(mb, begin, end, itr, tmp.data(), tmp.data() + tmp.size(), out_itr); + auto out_itr = &tmp.front(); + auto e = f.out(mb, begin, end, itr, &tmp.front(), &tmp.back() + 1, out_itr); ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - tmp.data()); + tmp.resize(out_itr - &tmp.front()); return tmp; } @@ -375,11 +375,11 @@ inline std::basic_string convert_chars( std::basic_string tmp(len, ' ', alloc); auto itr = begin; - auto out_itr = tmp.data(); - auto e = f.out(mb, begin, end, itr, tmp.data(), tmp.data() + tmp.size(), out_itr); + auto out_itr = &tmp.front(); + auto e = f.out(mb, begin, end, itr, &tmp.front(), &tmp.back() + 1, out_itr); ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - tmp.data()); + tmp.resize(out_itr - &tmp.front()); return tmp; } @@ -400,14 +400,13 @@ inline std::basic_string convert_chars( const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = f.length(mb, begin, end, std::numeric_limits::max()); - + const std::size_t len = f.length(mb, begin, end, (std::numeric_limits::max)()); std::basic_string res(len, u' ', alloc); auto itr = begin; - auto out_itr = res.data(); - auto e = f.in(mb, begin, end, itr, res.data(), res.data() + res.size(), out_itr); + auto out_itr = &res.front(); + auto e = f.in(mb, begin, end, itr, &res.front(), &res.back() + 1, out_itr); ec.assign(e, error::get_codecvt_category()); - res.resize(out_itr - res.data()); + res.resize(out_itr - &res.front()); return res; } @@ -427,15 +426,15 @@ inline std::basic_string convert_chars( const auto & f = std::use_facet >(loc); std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = f.length(mb, begin, end, std::numeric_limits::max()); + const std::size_t len = f.length(mb, begin, end, (std::numeric_limits::max)()); std::basic_string res(len, U' ', alloc); auto itr = begin; - auto out_itr = res.data(); - auto e = f.in(mb, begin, end, itr, res.data(), res.data() + res.size(), out_itr); + auto out_itr = &res.front(); + auto e = f.in(mb, begin, end, itr, &res.front(), &res.back() + 1, out_itr); ec.assign(e, error::get_codecvt_category()); - res.resize(out_itr - res.data()); + res.resize(out_itr - &res.front()); return res; } @@ -459,7 +458,7 @@ inline std::basic_string convert_chars( if (ec) return u""; - return convert_chars(ec, tmp.data(), tmp.data() + tmp.size(), u' ', alloc, loc); + return convert_chars(ec, &tmp.front(), &tmp.back() + 1, u' ', alloc, loc); } @@ -479,7 +478,7 @@ inline std::basic_string convert_chars( auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); if (ec) return U""; - return convert_chars(ec, tmp.data(), tmp.data() + tmp.size(), U' ', alloc, loc); + return convert_chars(ec, &tmp.front(), &tmp.back() + 1, U' ', alloc, loc); } @@ -499,7 +498,7 @@ inline std::basic_string convert_chars( auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); if (ec) return L""; - return convert_chars(ec, tmp.data(), tmp.data() + tmp.size(), L' ', alloc, loc); + return convert_chars(ec, &tmp.front(), &tmp.back() + 1, L' ', alloc, loc); } @@ -516,7 +515,7 @@ inline std::basic_string convert_chars( auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); if (ec) return L""; - return convert_chars(ec, tmp.data(), tmp.data() + tmp.size(), L' ', alloc, loc); + return convert_chars(ec, &tmp.front(), &tmp.back() + 1, L' ', alloc, loc); } diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp index 489099c94..9ccbbaf5a 100644 --- a/include/boost/process/v2/detail/config.hpp +++ b/include/boost/process/v2/detail/config.hpp @@ -14,6 +14,11 @@ #if defined(ASIO_WINDOWS) #define BOOST_PROCESS_V2_WINDOWS 1 + +// Windows: suppress definition of "min" and "max" macros. +#if !defined(NOMINMAX) +# define NOMINMAX 1 +#endif #endif #if defined(ASIO_HAS_UNISTD_H) @@ -22,6 +27,7 @@ #define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace process_v2 { #define BOOST_PROCESS_V2_END_NAMESPACE } +#define BOOST_PROCESS_V2_NAMESPACE process_v2 #else #include @@ -32,6 +38,12 @@ #if defined(BOOST_WINDOWS_API) #define BOOST_PROCESS_V2_WINDOWS 1 + +// Windows: suppress definition of "min" and "max" macros. +#if !defined(NOMINMAX) +# define NOMINMAX 1 +#endif + #endif #if defined(BOOST_POSIX_API) @@ -47,10 +59,9 @@ #include #endif -#define BOOST_PROCESS_V2_BEGIN_NAMESPACE \ -namespace boost { namespace process { namespace v2 { - +#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace boost { namespace process { namespace v2 { #define BOOST_PROCESS_V2_END_NAMESPACE } } } +#define BOOST_PROCESS_V2_NAMESPACE boost::process::v2 #endif diff --git a/include/boost/process/v2/detail/environment_win.hpp b/include/boost/process/v2/detail/environment_win.hpp index 04cc18c9d..e117efd3a 100644 --- a/include/boost/process/v2/detail/environment_win.hpp +++ b/include/boost/process/v2/detail/environment_win.hpp @@ -20,6 +20,7 @@ namespace environment { using char_type = wchar_t; + template struct key_char_traits { @@ -148,7 +149,7 @@ namespace detail { BOOST_PROCESS_V2_DECL -basic_cstring_ref> get( +std::basic_string> get( basic_cstring_ref> key, error_code & ec); @@ -163,7 +164,7 @@ void unset(basic_cstring_ref> key, BOOST_PROCESS_V2_DECL -basic_cstring_ref> get( +std::basic_string> get( basic_cstring_ref> key, error_code & ec); @@ -176,7 +177,7 @@ BOOST_PROCESS_V2_DECL void unset(basic_cstring_ref> key, error_code & ec); -inline native_handle_type load_native_handle(); +BOOST_PROCESS_V2_DECL native_handle_type load_native_handle(); struct native_handle_deleter { BOOST_PROCESS_V2_DECL void operator()(native_handle_type nh) const; @@ -187,6 +188,7 @@ inline const char_type * dereference(native_iterator iterator) {return iterator; BOOST_PROCESS_V2_DECL native_iterator next(native_handle_type nh); BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh); BOOST_PROCESS_V2_DECL bool is_executable(const filesystem::path & pth, error_code & ec); +} } BOOST_PROCESS_V2_END_NAMESPACE diff --git a/include/boost/process/v2/detail/impl/codecvt.ipp b/include/boost/process/v2/detail/impl/codecvt.ipp index 82eaf33dd..f4badba22 100644 --- a/include/boost/process/v2/detail/impl/codecvt.ipp +++ b/include/boost/process/v2/detail/impl/codecvt.ipp @@ -127,9 +127,18 @@ class windows_codecvt char* /*from*/, char* /*to*/, char* & /*next*/) const override { return ok; } int do_length(std::mbstate_t&, - const char* from, const char* from_end, std::size_t /*max*/) const override + const char* from, const char* from_end, std::size_t max) const override { - return std::distance(from, from_end); + auto codepage = +#if !defined(BOOST_NO_ANSI_APIS) + ::AreFileApisANSI() ? CP_ACP : +#endif + CP_OEMCP; + const auto res = ::MultiByteToWideChar(codepage, MB_PRECOMPOSED, + from, static_cast(from_end - from), + nullptr, 0); + + return static_cast((std::min)(static_cast(res), max)); } int do_max_length() const noexcept override { return 0; } @@ -137,7 +146,7 @@ class windows_codecvt BOOST_PROCESS_V2_DECL const std::codecvt< wchar_t, char, std::mbstate_t > & default_codecvt() { - const static const windows_codecvt cvt{1}; + const static windows_codecvt cvt{1}; return cvt; } diff --git a/include/boost/process/v2/detail/impl/environment.ipp b/include/boost/process/v2/detail/impl/environment.ipp index 7ef5d8fa9..3dc1b6615 100644 --- a/include/boost/process/v2/detail/impl/environment.ipp +++ b/include/boost/process/v2/detail/impl/environment.ipp @@ -2,8 +2,8 @@ // // 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_DETAIL_IMPL_ENVIRONMENT_HPP -#define BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_HPP +#ifndef BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_IPP +#define BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_IPP #include @@ -15,4 +15,4 @@ #error Operating System not supported. #endif -#endif //BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_HPP +#endif //BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_IPP diff --git a/include/boost/process/v2/detail/impl/environment_posix.ipp b/include/boost/process/v2/detail/impl/environment_posix.ipp index 67c4f4229..061ead334 100644 --- a/include/boost/process/v2/detail/impl/environment_posix.ipp +++ b/include/boost/process/v2/detail/impl/environment_posix.ipp @@ -34,7 +34,7 @@ basic_cstring_ref> get( auto res = ::getenv(key.c_str()); if (res == nullptr) { - ec = process::v2::detail::get_last_error(); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); return {}; } return res; @@ -45,13 +45,13 @@ void set(basic_cstring_ref> key, error_code & ec) { if (::setenv(key.c_str(), value.c_str(), true)) - ec = process::v2::detail::get_last_error(); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); } void unset(basic_cstring_ref> key, error_code & ec) { if (::unsetenv(key.c_str())) - ec = process::v2::detail::get_last_error(); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); } diff --git a/include/boost/process/v2/detail/impl/environment_win.ipp b/include/boost/process/v2/detail/impl/environment_win.ipp index 54bf2c30f..1826c1684 100644 --- a/include/boost/process/v2/detail/impl/environment_win.ipp +++ b/include/boost/process/v2/detail/impl/environment_win.ipp @@ -8,36 +8,33 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP -#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP +#ifndef BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_WIN_IPP +#define BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_WIN_IPP -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include "asio/detail/config.hpp" +#include +#include +#include +#include #include #include #include -#include -#include "asio/cstring_view.hpp" -#include "asio/error.hpp" +#include -#include "asio/detail/push_options.hpp" +#include +#include +BOOST_PROCESS_V2_BEGIN_NAMESPACE -namespace asio -{ namespace environment { namespace detail { std::basic_string> get( - ASIO_BASIC_CSTRING_VIEW_PARAM(char_type, key_char_traits) key, + basic_cstring_ref> key, error_code & ec) { std::basic_string> buf; @@ -46,36 +43,36 @@ std::basic_string> get( do { buf.resize(buf.size() + 4096); - size = ::GetEnvironmentVariableW(key.c_str(), buf.data(), buf.size()); + size = ::GetEnvironmentVariableW(key.c_str(), &buf.front(), static_cast(buf.size())); } while (size == buf.size()); buf.resize(size); if (buf.size() == 0) - ec.assign(::GetLastError(), asio::error::get_system_category()); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); return buf; } -void set(ASIO_BASIC_CSTRING_VIEW_PARAM(char_type, key_char_traits) key, - ASIO_BASIC_CSTRING_VIEW_PARAM(char_type, value_char_traits) value, +void set(basic_cstring_ref> key, + basic_cstring_ref> value, error_code & ec) { if (!::SetEnvironmentVariableW(key.c_str(), value.c_str())) - ec.assign(errno, asio::error::get_system_category()); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); } -void unset(ASIO_BASIC_CSTRING_VIEW_PARAM(char_type, key_char_traits) key, +void unset(basic_cstring_ref> key, error_code & ec) { if (!::SetEnvironmentVariableW(key.c_str(), nullptr)) - ec.assign(errno, asio::error::get_system_category()); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); } std::basic_string> get( - ASIO_BASIC_CSTRING_VIEW_PARAM(char, key_char_traits) key, + basic_cstring_ref> key, error_code & ec) { std::basic_string> buf; @@ -84,31 +81,31 @@ std::basic_string> get( do { buf.resize(buf.size() + 4096); - size = ::GetEnvironmentVariableA(key.c_str(), buf.data(), buf.size()); + size = ::GetEnvironmentVariableA(key.c_str(), &buf.front(), static_cast(buf.size())); } while (size == buf.size()); buf.resize(size); if (buf.size() == 0) - ec.assign(::GetLastError(), asio::error::get_system_category()); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); return buf; } -void set(ASIO_BASIC_CSTRING_VIEW_PARAM(char, key_char_traits) key, - ASIO_BASIC_CSTRING_VIEW_PARAM(char, value_char_traits) value, +void set(basic_cstring_ref> key, + basic_cstring_ref> value, error_code & ec) { if (!::SetEnvironmentVariableA(key.c_str(), value.c_str())) - ec.assign(errno, asio::error::get_system_category()); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); } -void unset(ASIO_BASIC_CSTRING_VIEW_PARAM(char, key_char_traits) key, +void unset(basic_cstring_ref> key, error_code & ec) { if (!::SetEnvironmentVariableA(key.c_str(), nullptr)) - ec.assign(errno, asio::error::get_system_category()); + ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); } @@ -133,15 +130,13 @@ native_iterator find_end(native_handle_type nh) return ++ ++nh; } -#if ASIO_HAS_FILESYSTEM -ASIO_DECL bool is_executable(const asio::filesystem::path & pth, error_code & ec) +bool is_executable(const filesystem::path & pth, error_code & ec) { - return asio::filesystem::is_regular_file(pth, ec) && SHGetFileInfoW(pth.native().c_str(), 0,0,0, SHGFI_EXETYPE); + return filesystem::is_regular_file(pth, ec) && SHGetFileInfoW(pth.native().c_str(), 0,0,0, SHGFI_EXETYPE); } -#endif } } -} +BOOST_PROCESS_V2_END_NAMESPACE -#endif //BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP +#endif //BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_WIN_IPP diff --git a/include/boost/process/v2/detail/last_error.hpp b/include/boost/process/v2/detail/last_error.hpp index 1eba544e8..05e09ee0a 100644 --- a/include/boost/process/v2/detail/last_error.hpp +++ b/include/boost/process/v2/detail/last_error.hpp @@ -8,6 +8,7 @@ #include BOOST_PROCESS_V2_BEGIN_NAMESPACE + namespace detail { BOOST_PROCESS_V2_DECL error_code get_last_error(); diff --git a/include/boost/process/v2/impl/error.ipp b/include/boost/process/v2/impl/error.ipp index 2bc52b676..d437539fc 100644 --- a/include/boost/process/v2/impl/error.ipp +++ b/include/boost/process/v2/impl/error.ipp @@ -42,7 +42,7 @@ struct codecvt_category : public error_category } // namespace detail -const error_category& get_codecvt_category() +BOOST_PROCESS_V2_DECL const error_category& get_codecvt_category() { static detail::codecvt_category instance; return instance; diff --git a/include/boost/process/v2/pid.hpp b/include/boost/process/v2/pid.hpp index 8ab1c9a34..5213fe0ce 100644 --- a/include/boost/process/v2/pid.hpp +++ b/include/boost/process/v2/pid.hpp @@ -25,9 +25,7 @@ BOOST_PROCESS_V2_DECL pid_type current_pid(); BOOST_PROCESS_V2_END_NAMESPACE #if defined(BOOST_PROCESS_V2_HEADER_ONLY) - #include - #endif diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam new file mode 100644 index 000000000..f665cb27f --- /dev/null +++ b/test/v2/Jamfile.jam @@ -0,0 +1,48 @@ +# 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) + + +import os ; + +if [ os.name ] = NT +{ + lib ws2_32 ; + lib shell32 ; + lib Advapi32 ; + lib Ntdll ; +} + +project : requirements + BOOST_ASIO_NO_DEPRECATED + msvc:_SCL_SECURE_NO_WARNINGS + msvc:_CRT_SECURE_NO_DEPRECATE + msvc:/bigobj + windows:WIN32_LEAN_AND_MEAN + linux:-lpthread + NT,cw:ws2_32 + NT,gcc:ws2_32 + BOOST_PROCESS_V2_SEPARATE_COMPILATION=1 +; + + +import testing ; + +alias filesystem : /boost//filesystem ; + +lib test_impl : test_impl.cpp filesystem : + BOOST_PROCESS_V2_SEPARATE_COMPILATION=1 + static + windows:shell32 + ; + +test-suite standalone : + [ run environment.cpp test_impl ] + [ run codecvt.cpp test_impl ] + [ run cstring_ref.cpp test_impl ] + [ run environment.cpp test_impl ] + [ run pid.cpp test_impl ] + ; + + diff --git a/test/v2/codecvt.cpp b/test/v2/codecvt.cpp index 52f74a66a..39700f254 100644 --- a/test/v2/codecvt.cpp +++ b/test/v2/codecvt.cpp @@ -62,8 +62,8 @@ BOOST_AUTO_TEST_CASE(test_codecvt) BOOST_CHECK(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), ' ') == in); BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), u8' ') == in8_t); - BOOST_CHECK_EQUAL(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), L' ') == win_t); - BOOST_CHECK_EQUAL(boost::process::v2::detail::convert_chars(win_t, end(win_t), u8' ') == in8_t); + BOOST_CHECK(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), L' ') == win_t); + BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), u8' ') == in8_t); #endif } diff --git a/test/v2/cstring_ref.cpp b/test/v2/cstring_ref.cpp index d575d9540..edac2e943 100644 --- a/test/v2/cstring_ref.cpp +++ b/test/v2/cstring_ref.cpp @@ -16,7 +16,7 @@ namespace bp2 = boost::process::v2; -template class bp2::basic_cstring_ref>; +template struct bp2::basic_cstring_ref>; using char_type = bp2::basic_cstring_ref>::const_pointer; From 47c4496d05c50ba7a6f37af97c45a434b5fda564 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Mon, 23 May 2022 16:12:28 +0200 Subject: [PATCH 051/397] Remove superflous calls in CMakeLists The find_package is not required, as the dependencies are done using the superproject build The include_directories is already there: target_include_directories --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20e7499fe..c0c1bfef9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,13 +6,11 @@ cmake_minimum_required(VERSION 3.5...3.16) project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) -find_package(Boost REQUIRED iostreams program_options filesystem system thread) add_library(boost_process INTERFACE) add_library(Boost::process ALIAS boost_process) target_include_directories(boost_process INTERFACE include) -include_directories(include) target_link_libraries(boost_process INTERFACE Boost::algorithm From 0fbfa1cdc15afba94431a312b7f3b7f4bfb97990 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Thu, 26 May 2022 15:01:01 +0800 Subject: [PATCH 052/397] Switched to pure utf8 support on windows. --- include/boost/process/v2.hpp | 12 + include/boost/process/v2/detail/codecvt.hpp | 578 ------------------ include/boost/process/v2/detail/config.hpp | 12 +- .../process/v2/detail/environment_win.hpp | 28 +- .../boost/process/v2/detail/impl/codecvt.ipp | 158 ----- .../v2/detail/impl/environment_win.ipp | 2 +- include/boost/process/v2/detail/utf8.hpp | 95 +++ include/boost/process/v2/environment.hpp | 381 ++++++------ include/boost/process/v2/error.hpp | 9 +- include/boost/process/v2/impl/error.ipp | 28 +- include/boost/process/v2/src.hpp | 3 +- test/v2/CMakeLists.txt | 6 +- test/v2/Jamfile.jam | 9 +- test/v2/codecvt.cpp | 69 --- test/v2/environment.cpp | 63 +- test/v2/header_1.cpp | 6 + test/v2/header_2.cpp | 6 + 17 files changed, 422 insertions(+), 1043 deletions(-) create mode 100644 include/boost/process/v2.hpp delete mode 100644 include/boost/process/v2/detail/codecvt.hpp delete mode 100644 include/boost/process/v2/detail/impl/codecvt.ipp create mode 100644 include/boost/process/v2/detail/utf8.hpp delete mode 100644 test/v2/codecvt.cpp create mode 100644 test/v2/header_1.cpp create mode 100644 test/v2/header_2.cpp diff --git a/include/boost/process/v2.hpp b/include/boost/process/v2.hpp new file mode 100644 index 000000000..bdb995c4b --- /dev/null +++ b/include/boost/process/v2.hpp @@ -0,0 +1,12 @@ +// 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_HPP +#define BOOST_PROCESS_V2_HPP + +#include +#include +#include + +#endif //BOOST_PROCESS_V2_HPP diff --git a/include/boost/process/v2/detail/codecvt.hpp b/include/boost/process/v2/detail/codecvt.hpp deleted file mode 100644 index 935c275c8..000000000 --- a/include/boost/process/v2/detail/codecvt.hpp +++ /dev/null @@ -1,578 +0,0 @@ -// 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_DETAIL_CODECVT_HPP -#define BOOST_PROCESS_V2_DETAIL_CODECVT_HPP - -#include -#include -#include - -#include - -BOOST_PROCESS_V2_BEGIN_NAMESPACE - - -namespace detail -{ - -#if defined(BOOST_PROCESS_V2_WINDOWS) - -BOOST_PROCESS_V2_DECL const std::codecvt< wchar_t, char, std::mbstate_t > & default_codecvt(); - -#else - -inline const std::codecvt< wchar_t, char, std::mbstate_t > & default_codecvt() -{ - return std::use_facet>(std::locale()); -} - -#endif - -// Needed conversions [char8_t, char16_t, char32_t, wchar_t, char] <-> [char, wchar_t] - -// C++20 - -//std::codecvt identity conversion -//std::codecvt conversion between the system's native wide and the single-byte narrow character sets - -//std::codecvt conversion between UTF-16 and UTF-8 (since C++20) -//std::codecvt conversion between UTF-32 and UTF-8 (since C++20) - - -// C++17 - -//std::codecvt identity conversion -//std::codecvt conversion between the system's native wide and the single-byte narrow character sets - -//std::codecvt conversion between UTF-16 and UTF-8 (since C++11)(deprecated in C++20) -//std::codecvt conversion between UTF-32 and UTF-8 (since C++11)(deprecated in C++20) - -template< class Traits, class Char, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &, - const Char * begin, - const Char * end, - Char, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - return std::basic_string(begin, end, alloc); -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const wchar_t * begin, - const wchar_t * end, - char, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = loc == std::locale() - ? default_codecvt() - : std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = (end - begin) * 2; - std::basic_string tmp(len, ' ', alloc); - - auto itr = begin; - auto out_itr = &tmp.front(); - auto e = f.out(mb, begin, end, itr, &tmp.front(), &tmp.back() + 1, out_itr); - ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - &tmp.front()); - return tmp; -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const char * begin, - const char * end, - wchar_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = loc == std::locale() - ? default_codecvt() - : std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = f.length(mb, begin, end, (std::numeric_limits::max)()); - - std::basic_string res(len, L' ', alloc); - auto itr = begin; - auto out_itr = &res.front(); - auto e = f.in(mb, begin, end, itr, &res.front(), &res.back() + 1, out_itr); - ec.assign(e, error::get_codecvt_category()); - res.resize(out_itr - &res.front()); - return res; -} - - -#if defined(BOOST_PROCESS_V2_HAS_CHAR8_T) - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &, - const char * begin, - const char * end, - char8_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - return std::basic_string(begin, end, alloc); -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &, - const char8_t * begin, - const char8_t * end, - char, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - return std::basic_string(begin, end, alloc); -} - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const char8_t * begin, - const char8_t * end, - wchar_t w, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - return convert_chars(ec, - reinterpret_cast(begin), - reinterpret_cast(end), w, alloc, loc); -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const wchar_t * begin, - const wchar_t * end, - char8_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = loc == std::locale() - ? default_codecvt() - : std::use_facet >(loc) - ; - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = (end - begin) * 2; - std::basic_string tmp(len, ' ', alloc); - - auto itr = begin; - auto out_itr = &tmp.front(); - auto e = f.out(mb, begin, end, itr, - reinterpret_cast(&tmp.front()), - reinterpret_cast(&tmp.back()), - reinterpret_cast(out_itr)); - ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - &tmp.front()); - - return tmp; -} - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const char16_t * begin, - const char16_t * end, - char, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = (end - begin) * 2; - std::basic_string tmp(len, ' ', alloc); - - auto itr = begin; - auto out_itr = &tmp.front(); - auto e = f.out(mb, begin, end, itr, - reinterpret_cast(&tmp.front()), - reinterpret_cast(&tmp.back()), - reinterpret_cast(out_itr)); - - ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - &tmp.front()); - - return tmp; -} - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const char32_t * begin, - const char32_t * end, - char, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = (end - begin) * 4; - std::basic_string tmp(len, ' ', alloc); - - auto itr = begin; - auto out_itr = &tmp.front(); - auto e = f.out(mb, begin, end, itr, - reinterpret_cast(&tmp.front()), - reinterpret_cast(&tmp.back()), - reinterpret_cast(out_itr)); - ec.assign(e, error::get_codecvt_category()); - - tmp.resize(out_itr - &tmp.front()); - - return tmp; -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &ec, - const char * begin, - const char * end, - char16_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = f.length(mb, reinterpret_cast(begin), - reinterpret_cast(end), - std::numeric_limits::max()); - - std::basic_string res(len, u' ', alloc); - auto itr = begin; - auto out_itr = &res.front(); - auto e = f.in(mb, - reinterpret_cast(begin), - reinterpret_cast(end), - reinterpret_cast(itr), - &res.front(), &res.back() + 1, out_itr); - ec.assign(e, error::get_codecvt_category()); - res.resize(out_itr - &res.front()); - return res; -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const char * begin, - const char * end, - char32_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = f.length(mb, - reinterpret_cast(begin), - reinterpret_cast(end), - std::numeric_limits::max()); - - std::basic_string res(len, U' ', alloc); - auto itr = begin; - auto out_itr = &res.front(); - auto e = f.in(mb, - reinterpret_cast(begin), - reinterpret_cast(end), - reinterpret_cast(itr), - &res.front(), &res.back() + 1, out_itr); - ec.assign(e, error::get_codecvt_category()); - - res.resize(out_itr - &res.front()); - return res; -} - - -#else - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const char16_t * begin, - const char16_t * end, - char, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = (end - begin) * 2; - std::basic_string tmp(len, ' ', alloc); - - auto itr = begin; - auto out_itr = &tmp.front(); - auto e = f.out(mb, begin, end, itr, &tmp.front(), &tmp.back() + 1, out_itr); - - ec.assign(e, error::get_codecvt_category()); - tmp.resize(out_itr - &tmp.front()); - - return tmp; -} - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const char32_t * begin, - const char32_t * end, - char, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = (end - begin) * 4; - std::basic_string tmp(len, ' ', alloc); - - auto itr = begin; - auto out_itr = &tmp.front(); - auto e = f.out(mb, begin, end, itr, &tmp.front(), &tmp.back() + 1, out_itr); - ec.assign(e, error::get_codecvt_category()); - - tmp.resize(out_itr - &tmp.front()); - - return tmp; -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &ec, - const char * begin, - const char * end, - char16_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = f.length(mb, begin, end, (std::numeric_limits::max)()); - std::basic_string res(len, u' ', alloc); - auto itr = begin; - auto out_itr = &res.front(); - auto e = f.in(mb, begin, end, itr, &res.front(), &res.back() + 1, out_itr); - ec.assign(e, error::get_codecvt_category()); - res.resize(out_itr - &res.front()); - return res; -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code & ec, - const char * begin, - const char * end, - char32_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - const auto & f = std::use_facet >(loc); - - std::mbstate_t mb = std::mbstate_t(); - const std::size_t len = f.length(mb, begin, end, (std::numeric_limits::max)()); - - std::basic_string res(len, U' ', alloc); - auto itr = begin; - auto out_itr = &res.front(); - auto e = f.in(mb, begin, end, itr, &res.front(), &res.back() + 1, out_itr); - ec.assign(e, error::get_codecvt_category()); - - res.resize(out_itr - &res.front()); - return res; -} - -#endif - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &ec, - const wchar_t * begin, - const wchar_t * end, - char16_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - using rebind_alloc = typename std::allocator_traits::template rebind_alloc; - auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); - - if (ec) - return u""; - - return convert_chars(ec, &tmp.front(), &tmp.back() + 1, u' ', alloc, loc); -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &ec, - const wchar_t * begin, - const wchar_t * end, - char32_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - using rebind_alloc = typename std::allocator_traits::template rebind_alloc; - auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); - if (ec) - return U""; - return convert_chars(ec, &tmp.front(), &tmp.back() + 1, U' ', alloc, loc); -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &ec, - const char16_t * begin, - const char16_t * end, - wchar_t, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - if (begin == end) - return {}; - - using rebind_alloc = typename std::allocator_traits::template rebind_alloc; - auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); - if (ec) - return L""; - return convert_chars(ec, &tmp.front(), &tmp.back() + 1, L' ', alloc, loc); -} - - -template< class Traits, class Alloc = std::allocator> -inline std::basic_string convert_chars( - error_code &ec, - const char32_t * begin, - const char32_t * end, - wchar_t w, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - using rebind_alloc = typename std::allocator_traits::template rebind_alloc; - auto tmp = convert_chars>(ec, begin, end, ' ', rebind_alloc(alloc), loc); - if (ec) - return L""; - return convert_chars(ec, &tmp.front(), &tmp.back() + 1, L' ', alloc, loc); -} - - - -template< class Traits, class CharIn, class CharOut, class Alloc = std::allocator> -inline std::basic_string, Alloc> convert_chars( - const CharIn * begin, - const CharIn * end, - CharOut c, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - error_code ec; - auto res = convert_chars(ec, begin, end, c, alloc, loc); - if (ec) - detail::throw_error(ec, "convert_chars"); - return res; -} - -template< class CharIn, class CharOut, class Alloc = std::allocator> -inline std::basic_string, Alloc> convert_chars( - error_code & ec, - const CharIn * begin, - const CharIn * end, - CharOut c, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - return convert_chars>(ec, begin, end, c, alloc, loc); -} - -template< class CharIn, class CharOut, class Alloc = std::allocator> -inline std::basic_string, Alloc> convert_chars( - const CharIn * begin, - const CharIn * end, - CharOut c, - const Alloc & alloc = Alloc(), - const std::locale &loc = std::locale()) -{ - error_code ec; - auto res = convert_chars>(ec, begin, end, c, alloc, loc); - if (ec) - detail::throw_error(ec, "convert_chars"); - return res; -} - - - -} // detail - - -BOOST_PROCESS_V2_END_NAMESPACE - -#if defined(BOOST_PROCESS_V2_HEADER_ONLY) - -#include - -#endif - -#endif //BOOST_PROCESS_V2_DETAIL_CODECVT_HPP diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp index 9ccbbaf5a..c7d75e7df 100644 --- a/include/boost/process/v2/detail/config.hpp +++ b/include/boost/process/v2/detail/config.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #if defined(ASIO_WINDOWS) @@ -50,6 +51,10 @@ #define BOOST_PROCESS_V2_POSIX 1 #endif +#if !defined(BOOST_PROCESS_V2_WINDOWS) && !defined(BOOST_POSIX_API) +#error Unsupported operating system +#endif + #if defined(BOOST_PROCESS_USE_STD_FS) #include #include @@ -65,13 +70,6 @@ #endif -#if !defined(BOOST_PROCESS_HAS_CHAR8_T) -# if (__cplusplus >= 202002) -# define BOOST_PROCESS_HAS_CHAR8_T 1 -# endif -#endif - - BOOST_PROCESS_V2_BEGIN_NAMESPACE #if defined(BOOST_PROCESS_STANDALONE) diff --git a/include/boost/process/v2/detail/environment_win.hpp b/include/boost/process/v2/detail/environment_win.hpp index e117efd3a..795aef138 100644 --- a/include/boost/process/v2/detail/environment_win.hpp +++ b/include/boost/process/v2/detail/environment_win.hpp @@ -14,6 +14,8 @@ #include #include +#include + BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace environment @@ -30,11 +32,11 @@ struct key_char_traits typedef typename std::char_traits::pos_type pos_type; typedef typename std::char_traits::state_type state_type; - BOOST_CONSTEXPR static char to_upper(char c) {return std::toupper(to_int_type(c));} - BOOST_CONSTEXPR static wchar_t to_upper(wchar_t c) {return std::towupper(to_int_type(c));} + BOOST_CONSTEXPR static char to_lower(char c) {return std::tolower(to_int_type(c));} + BOOST_CONSTEXPR static wchar_t to_lower(wchar_t c) {return std::towlower(to_int_type(c));} - BOOST_CONSTEXPR static int_type to_upper(int_type i, char ) {return std::toupper(i);} - BOOST_CONSTEXPR static int_type to_upper(int_type i, wchar_t) {return std::towupper(i);} + BOOST_CONSTEXPR static int_type to_lower(int_type i, char ) {return std::tolower(i);} + BOOST_CONSTEXPR static int_type to_lower(int_type i, wchar_t) {return std::towlower(i);} BOOST_CONSTEXPR static @@ -46,13 +48,13 @@ struct key_char_traits BOOST_CONSTEXPR static bool eq(char_type c1, char_type c2) BOOST_NOEXCEPT { - return to_upper(c1) == to_upper(c2); + return to_lower(c1) == to_lower(c2); } BOOST_CONSTEXPR static bool lt(char_type c1, char_type c2) BOOST_NOEXCEPT { - return to_upper(c1) < to_upper(c2); + return to_lower(c1) < to_lower(c2); } BOOST_CONSTEXPR static @@ -61,8 +63,8 @@ struct key_char_traits auto itrs = std::mismatch(s1, s1 + n, s2, &eq); if (itrs.first == (s1 + n)) return 0; - auto c1 = to_upper(*itrs.first); - auto c2 = to_upper(*itrs.second); + auto c1 = to_lower(*itrs.first); + auto c2 = to_lower(*itrs.second); return (c1 < c2 ) ? -1 : 1; } @@ -73,8 +75,8 @@ struct key_char_traits BOOST_CONSTEXPR static const char_type* find(const char_type* s, size_t n, const char_type& a) BOOST_NOEXCEPT { - const char_type u = to_upper(a); - return std::find_if(s, s + n, [u](char_type c){return to_upper(c) == u;}); + const char_type u = to_lower(a); + return std::find_if(s, s + n, [u](char_type c){return to_lower(c) == u;}); } BOOST_CONSTEXPR static @@ -126,7 +128,7 @@ struct key_char_traits BOOST_CONSTEXPR static bool eq_int_type(int_type c1, int_type c2) BOOST_NOEXCEPT { - return to_upper(c1, char_type()) == to_upper(c2, char_type()); + return to_lower(c1, char_type()) == to_lower(c2, char_type()); } BOOST_CONSTEXPR static inline int_type eof() BOOST_NOEXCEPT @@ -180,12 +182,14 @@ void unset(basic_cstring_ref> key, BOOST_PROCESS_V2_DECL native_handle_type load_native_handle(); struct native_handle_deleter { + native_handle_deleter() = default; + native_handle_deleter(const native_handle_deleter& ) = default; BOOST_PROCESS_V2_DECL void operator()(native_handle_type nh) const; }; inline const char_type * dereference(native_iterator iterator) {return iterator;} -BOOST_PROCESS_V2_DECL native_iterator next(native_handle_type nh); +BOOST_PROCESS_V2_DECL native_iterator next(native_iterator nh); BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh); BOOST_PROCESS_V2_DECL bool is_executable(const filesystem::path & pth, error_code & ec); } diff --git a/include/boost/process/v2/detail/impl/codecvt.ipp b/include/boost/process/v2/detail/impl/codecvt.ipp deleted file mode 100644 index f4badba22..000000000 --- a/include/boost/process/v2/detail/impl/codecvt.ipp +++ /dev/null @@ -1,158 +0,0 @@ -// 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_DETAIL_IMPL_CODECVT_IPP -#define BOOST_PROCESS_V2_DETAIL_IMPL_CODECVT_IPP - -#if defined(BOOST_PROCESS_V2_WINDOWS) - -#include -#include - -#include - -BOOST_PROCESS_V2_BEGIN_NAMESPACE -namespace detail { - - -//copied from boost.filesystem -class windows_codecvt - : public std::codecvt< wchar_t, char, std::mbstate_t > -{ - public: - explicit windows_codecvt(std::size_t ref_count = 0) - : std::codecvt(ref_count) {} - protected: - - bool do_always_noconv() const noexcept override { return false; } - - // seems safest to assume variable number of characters since we don't - // actually know what codepage is active - int do_encoding() const noexcept override { return 0; } - - std::codecvt_base::result do_in(std::mbstate_t& state, - const char* from, const char* from_end, const char*& from_next, - wchar_t* to, wchar_t* to_end, wchar_t*& to_next) const override - { - - auto codepage = -#if !defined(BOOST_NO_ANSI_APIS) - ::AreFileApisANSI() ? CP_ACP : -#endif - CP_OEMCP; - - result res = ok; - int count = 0; - if ((count = ::MultiByteToWideChar(codepage, MB_PRECOMPOSED, - from, static_cast(from_end - from), - to, static_cast(to_end - to))) == 0) - { - switch (::GetLastError()) - { - case ERROR_INSUFFICIENT_BUFFER: - // A supplied buffer size was not large enough, or it was incorrectly set to NULL. - res = partial; - break; - case ERROR_INVALID_FLAGS: - // The values supplied for flags were not valid. - res = error; - break; - case ERROR_INVALID_PARAMETER: - // Any of the parameter values was invalid. - res = error; - break; - case ERROR_NO_UNICODE_TRANSLATION: - // Invalid Unicode was found in a string. - res = error; - break; - } - } - if (res != error) - { - from_next = from_end; - to_next = to + count; - *to_next = L'\0'; - } - return res; - } - - std::codecvt_base::result do_out(std::mbstate_t & state, - const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, - char* to, char* to_end, char*& to_next) const override - { - auto codepage = -#if !defined(BOOST_NO_ANSI_APIS) - ::AreFileApisANSI() ? CP_ACP : -#endif - CP_OEMCP; - result res = ok; - - int count = 0; - - - if ((count = WideCharToMultiByte(codepage, - WC_NO_BEST_FIT_CHARS, from, - static_cast(from_end - from), to, static_cast(to_end - to), 0, 0)) == 0) - { - switch (::GetLastError()) - { - case ERROR_INSUFFICIENT_BUFFER: - // A supplied buffer size was not large enough, or it was incorrectly set to NULL. - res = partial; - break; - case ERROR_INVALID_FLAGS: - // The values supplied for flags were not valid. - res = error; - break; - case ERROR_INVALID_PARAMETER: - // Any of the parameter values was invalid. - res = error; - break; - case ERROR_NO_UNICODE_TRANSLATION: - // Invalid Unicode was found in a string. - res = error; - break; - } } - if (res != error) - { - from_next = from_end; - to_next = to + count; - *to_next = '\0'; - } - return res; - } - - std::codecvt_base::result do_unshift(std::mbstate_t&, - char* /*from*/, char* /*to*/, char* & /*next*/) const override { return ok; } - - int do_length(std::mbstate_t&, - const char* from, const char* from_end, std::size_t max) const override - { - auto codepage = -#if !defined(BOOST_NO_ANSI_APIS) - ::AreFileApisANSI() ? CP_ACP : -#endif - CP_OEMCP; - const auto res = ::MultiByteToWideChar(codepage, MB_PRECOMPOSED, - from, static_cast(from_end - from), - nullptr, 0); - - return static_cast((std::min)(static_cast(res), max)); - } - - int do_max_length() const noexcept override { return 0; } - }; - -BOOST_PROCESS_V2_DECL const std::codecvt< wchar_t, char, std::mbstate_t > & default_codecvt() -{ - const static windows_codecvt cvt{1}; - return cvt; -} - -} -BOOST_PROCESS_V2_END_NAMESPACE - -#endif - -#endif //BOOST_PROCESS_V2_DETAIL_IMPL_CODECVT_IPP diff --git a/include/boost/process/v2/detail/impl/environment_win.ipp b/include/boost/process/v2/detail/impl/environment_win.ipp index 1826c1684..37e72dc26 100644 --- a/include/boost/process/v2/detail/impl/environment_win.ipp +++ b/include/boost/process/v2/detail/impl/environment_win.ipp @@ -127,7 +127,7 @@ native_iterator find_end(native_handle_type nh) { while ((*nh != L'\0') || (*std::next(nh) != L'\0')) nh++; - return ++ ++nh; + return ++nh; } bool is_executable(const filesystem::path & pth, error_code & ec) diff --git a/include/boost/process/v2/detail/utf8.hpp b/include/boost/process/v2/detail/utf8.hpp new file mode 100644 index 000000000..edceeb056 --- /dev/null +++ b/include/boost/process/v2/detail/utf8.hpp @@ -0,0 +1,95 @@ +// 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_DETAIL_UTF8_HPP +#define BOOST_PROCESS_V2_DETAIL_UTF8_HPP + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +BOOST_PROCESS_V2_DECL std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec); +BOOST_PROCESS_V2_DECL std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec); + +BOOST_PROCESS_V2_DECL std::size_t convert_to_utf8(const wchar_t * in, std::size_t size, + char * out, std::size_t max_size, error_code & ec); +BOOST_PROCESS_V2_DECL std::size_t convert_to_wide(const char * in, std::size_t size, + wchar_t * out, std::size_t max_size, error_code & ec); + +template, + typename Allocator = std::allocator, typename CharIn, + typename = typename std::enable_if::value>::type> +BOOST_PROCESS_V2_DECL +std::basic_string conv_string( + const CharIn * data, std::size_t size, + const Allocator allocator = Allocator{}) +{ + return std::basic_string(data, size, allocator); +} + + +template, + typename Allocator = std::allocator, + typename = typename std::enable_if::value>::type> +BOOST_PROCESS_V2_DECL +std::basic_string conv_string( + const wchar_t * data, std::size_t size, + const Allocator allocator = Allocator{}) +{ + error_code ec; + const auto req_size = size_as_utf8(data, size, ec); + if (ec) + detail::throw_error(ec, "size_as_utf8"); + + std::basic_string res(allocator); + res.resize(req_size); + + auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec); + if (ec) + detail::throw_error(ec, "convert_to_utf8"); + + res.resize(res_size); + return res; +} + +template, + typename Allocator = std::allocator, + typename = typename std::enable_if::value>::type> +BOOST_PROCESS_V2_DECL +std::basic_string conv_string( + const char * data, std::size_t size, + const Allocator allocator = Allocator{}) +{ + error_code ec; + const auto req_size = size_as_wide(data, size, ec); + if (ec) + detail::throw_error(ec, "size_as_wide"); + + std::basic_string res(allocator); + res.resize(req_size); + + auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec); + if (ec) + detail::throw_error(ec, "convert_to_wide"); + + res.resize(res_size); + return res; +} + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + + +#endif //BOOST_PROCESS_V2_DETAIL_UTF8_HPP diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index d410c30d2..f6119d9eb 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -128,7 +128,8 @@ struct key_view key_view() noexcept = default; key_view( const key_view& p ) = default; key_view( key_view&& p ) noexcept = default; - template::value>::type> + template::value>::type> key_view( const Source& source ) : value_(source) {} key_view( const char_type * p) : value_(p) {} key_view( char_type * p) : value_(p) {} @@ -159,25 +160,20 @@ struct key_view template< class CharT, class Traits = std::char_traits, class Alloc = std::allocator > std::basic_string - string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale()) const + basic_string( const Alloc& alloc = Alloc()) const { - return boost::process::v2::detail::convert_chars(value_.data(), value_.data() + value_.size(), CharT(), alloc, loc); + return boost::process::v2::detail::conv_string( + value_.data(), value_.size(), alloc); } - std::string string() const {return string();} - std::wstring wstring() const {return string();} - std::u16string u16string() const {return string();} - std::u32string u32string() const {return string();} + std::string string() const {return basic_string();} + std::wstring wstring() const {return basic_string();} string_type native_string() const { - return string>(); + return basic_string>(); } -#if BOOST_PROCESS_HAS_CHAR8_T - std::u8string u8string() const {return string();} -#endif - friend bool operator==(key_view l, key_view r) { return l.value_ == r.value_; } friend bool operator!=(key_view l, key_view r) { return l.value_ != r.value_; } friend bool operator<=(key_view l, key_view r) { return l.value_ <= r.value_; } @@ -191,7 +187,7 @@ struct key_view friend std::basic_ostream& operator<<( std::basic_ostream& os, const key_view& p ) { - os << boost::process::v2::quoted(p.string()); + os << boost::process::v2::quoted(p.basic_string()); return os; } @@ -252,25 +248,20 @@ struct value_view template< class CharT, class Traits = std::char_traits, class Alloc = std::allocator > std::basic_string - string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale() ) const + basic_string( const Alloc& alloc = Alloc() ) const { - return boost::process::v2::detail::convert_chars(value_.begin(), value_.end(), CharT(), alloc, loc); + return boost::process::v2::detail::conv_string( + value_.data(), value_.size(), alloc); } - std::string string() const {return string();} - std::wstring wstring() const {return string();} - std::u16string u16string() const {return string();} - std::u32string u32string() const {return string();} + std::string string() const {return basic_string();} + std::wstring wstring() const {return basic_string();} string_type native_string() const { - return string>(); + return basic_string>(); } -#if BOOST_PROCESS_HAS_CHAR8_T - std::u8string u8string() const {return string();} -#endif - bool empty() const {return value_.empty(); } friend bool operator==(value_view l, value_view r) { return l.value_ == r.value_; } @@ -285,7 +276,7 @@ struct value_view friend std::basic_ostream& operator<<( std::basic_ostream& os, const value_view& p ) { - os << boost::process::v2::quoted(p.string()); + os << boost::process::v2::quoted(p.basic_string()); return os; } @@ -319,7 +310,8 @@ struct key_value_pair_view key_value_pair_view() noexcept = default; key_value_pair_view( const key_value_pair_view& p ) = default; key_value_pair_view( key_value_pair_view&& p ) noexcept = default; - template::value>::type> + template::value>::type> key_value_pair_view( const Source& source ) : value_(source) {} key_value_pair_view( const char_type * p) : value_(p) {} @@ -341,58 +333,81 @@ struct key_value_pair_view operator string_view_type() const {return native();} operator typename string_view_type::string_view_type() const {return value_; } - int compare( const key_value_pair_view& p ) const noexcept {return value_.compare(p.value_);} - int compare( const string_type& str ) const {return value_.compare(str);} - int compare( string_view_type str ) const {return value_.compare(str);} - int compare( const value_type* s ) const {return value_.compare(s);} + int compare( key_value_pair_view p ) const noexcept + { + const auto c = key().compare(p.key()); + if (c != 0) + return c; + return value().compare(p.value()); + } + int compare( const string_type& str ) const + { + return compare(key_value_pair_view(str)); + } + int compare( string_view_type str ) const + { + string_type st(str.data(), str.size()); + return compare(st); + } + int compare( const value_type* s ) const + { + return compare(key_value_pair_view(s)); + } template< class CharT, class Traits = std::char_traits, class Alloc = std::allocator > std::basic_string - string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale()) const + basic_string( const Alloc& alloc = Alloc()) const { - return boost::process::v2::detail::convert_chars(value_.begin(), value_.end(), CharT(), alloc, loc); + return boost::process::v2::detail::conv_string(value_.begin(), value_.size(), alloc); } - std::string string() const {return string();} - std::wstring wstring() const {return string();} - std::u16string u16string() const {return string();} - std::u32string u32string() const {return string();} + std::string string() const {return basic_string();} + std::wstring wstring() const {return basic_string();} string_type native_string() const { - return string(); + return basic_string(); } -#if BOOST_PROCESS_HAS_CHAR8_T - std::u8string u8string() const {return string();} -#endif - bool empty() const {return value_.empty(); } key_view key() const { - const auto eq = value_.find(equality_sign); + auto eq = value_.find(equality_sign); + if (eq == 0) + { + auto eq2 = value_.find(equality_sign, 1); + if (eq2 != string_type::npos) + eq = eq2; + } const auto res = native().substr(0, eq == string_view_type::npos ? value_.size() : eq); return key_view::string_view_type(res.data(), res.size()); } value_view value() const { - return environment::value_view(native().substr(value_.find(equality_sign) + 1)); + auto eq = value_.find(equality_sign); + if (eq == 0) + { + auto eq2 = value_.find(equality_sign, 1); + if (eq2 != string_type::npos) + eq = eq2; + } + return environment::value_view(native().substr(eq + 1)); } - friend bool operator==(key_value_pair_view l, key_value_pair_view r) { return l.value_ == r.value_; } - friend bool operator!=(key_value_pair_view l, key_value_pair_view r) { return l.value_ != r.value_; } - friend bool operator<=(key_value_pair_view l, key_value_pair_view r) { return l.value_ <= r.value_; } - friend bool operator>=(key_value_pair_view l, key_value_pair_view r) { return l.value_ >= r.value_; } - friend bool operator< (key_value_pair_view l, key_value_pair_view r) { return l.value_ < r.value_; } - friend bool operator> (key_value_pair_view l, key_value_pair_view r) { return l.value_ > r.value_; } + friend bool operator==(key_value_pair_view l, key_value_pair_view r) { return l.compare(r) == 0; } + friend bool operator!=(key_value_pair_view l, key_value_pair_view r) { return l.compare(r) != 0; } + friend bool operator<=(key_value_pair_view l, key_value_pair_view r) { return l.compare(r) <= 0; } + friend bool operator>=(key_value_pair_view l, key_value_pair_view r) { return l.compare(r) >= 0; } + friend bool operator< (key_value_pair_view l, key_value_pair_view r) { return l.compare(r) < 0; } + friend bool operator> (key_value_pair_view l, key_value_pair_view r) { return l.compare(r) > 0; } template< class CharT, class Traits > friend std::basic_ostream& operator<<( std::basic_ostream& os, const key_value_pair_view& p ) { - os << boost::process::v2::quoted(p.string()); + os << boost::process::v2::quoted(p.basic_string()); return os; } @@ -448,38 +463,35 @@ struct key key( const value_type * raw ) : value_(raw) {} key( value_type * raw ) : value_(raw) {} - explicit key(key_view kv) : value_(kv.string()) {} + explicit key(key_view kv) : value_(kv.native_string()) {} template< class Source > - key( const Source& source, const std::locale& loc = std::locale(), + key( const Source& source, const std::locale& loc, decltype(source.data()) = nullptr) - : value_(boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc)) + : value_( + boost::process::v2::detail::conv_string(source.data(), source.size())) { } - key(const typename conditional::value, wchar_t, char>::type * raw, const std::locale& loc = std::locale()) - : value_(boost::process::v2::detail::convert_chars( - raw, - raw + std::char_traits::type>::type>::length(raw), - char_type(), std::allocator(), loc)) + template< class Source > + key( const Source& source, + decltype(source.data()) = nullptr, + decltype(source.size()) = 0u) + : value_( + boost::process::v2::detail::conv_string( + source.data(), source.size())) { } - key(const char16_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} - key(const char32_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} -#if BOOST_PROCESS_HAS_CHAR8_T - key(const char8_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} -#endif - - template - key(std::basic_string_view source, const std::locale& loc = std::locale()) - : value_(boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc)) + key(const typename conditional::value, wchar_t, char>::type * raw) + : value_(boost::process::v2::detail::conv_string( + raw, std::char_traits::type>::type>::length(raw))) { } template< class InputIt > - key( InputIt first, InputIt last, const std::locale& loc = std::locale()) - : key(std::basic_string(first, last), loc) + key( InputIt first, InputIt last) + : key(std::basic_string(first, last)) { } @@ -495,7 +507,7 @@ struct key template< class Source > key& operator=( const Source& source ) { - value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator()); + value_ = boost::process::v2::detail::conv_string(source.data(), source.size()); return *this; } @@ -505,9 +517,9 @@ struct key return *this; } template< class Source > - key& assign( const Source& source , const std::locale & loc) + key& assign( const Source& source ) { - value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc); + value_ = boost::process::v2::detail::conv_string(source.data(), source.size()); return *this; } @@ -537,27 +549,23 @@ struct key int compare( const value_type* s ) const {return value_.compare(s);} template< class CharT, class Traits = std::char_traits, - class Alloc = std::allocator > + class Alloc = std::allocator > std::basic_string - string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale() ) const + basic_string( const Alloc& alloc = Alloc()) const { - return boost::process::v2::detail::convert_chars(value_.data(), value_.data() + value_.size(), CharT(), alloc, loc); + return boost::process::v2::detail::conv_string( + value_.data(), value_.size(), alloc); } - std::string string() const {return string();} - std::wstring wstring() const {return string();} - std::u16string u16string() const {return string();} - std::u32string u32string() const {return string();} - std::basic_string> native_string() const + std::string string() const {return basic_string();} + std::wstring wstring() const {return basic_string();} + + const string_type & native_string() const { - return string>(); + return value_; } -#if BOOST_PROCESS_HAS_CHAR8_T - std::u8string u8string() const {return string();} -#endif - bool empty() const {return value_.empty(); } friend bool operator==(const key & l, const key & r) { return l.value_ == r.value_; } @@ -571,7 +579,7 @@ struct key friend std::basic_ostream& operator<<( std::basic_ostream& os, const key& p ) { - os << boost::process::v2::quoted(p.string()); + os << boost::process::v2::quoted(p.basic_string()); return os; } @@ -671,29 +679,23 @@ struct value explicit value(value_view kv) : value_(kv.c_str()) {} template< class Source > - value( const Source& source, const std::locale& loc = std::locale(), - decltype(source.data()) = nullptr) - : value_(boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc)) + value( const Source& source, + decltype(source.data()) = nullptr, + decltype(source.size()) = 0u) + : value_(boost::process::v2::detail::conv_string( + source.data(), source.data() + source.size())) { } - value(const typename conditional::value, wchar_t, char>::type * raw, const std::locale& loc = std::locale()) - : value_(boost::process::v2::detail::convert_chars( - raw, - raw + std::char_traits::type>::type>::length(raw), - char_type(), std::allocator(), loc)) + value(const typename conditional::value, wchar_t, char>::type * raw) + : value_(boost::process::v2::detail::conv_string( + raw, std::char_traits::type>::type>::length(raw))) { } - value(const char16_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} - value(const char32_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} -#if BOOST_PROCESS_HAS_CHAR8_T - value(const char8_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} -#endif - template< class InputIt > - value( InputIt first, InputIt last, const std::locale& loc = std::locale()) - : value(std::basic_string(first, last), loc) + value( InputIt first, InputIt last) + : value(std::basic_string(first, last)) { } @@ -709,7 +711,8 @@ struct value template< class Source > value& operator=( const Source& source ) { - value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator()); + value_ = boost::process::v2::detail::conv_string( + source.data(), source.size); return *this; } @@ -719,10 +722,11 @@ struct value return *this; } template< class Source > - value& assign( const Source& source, const std::locale & loc = std::locale() ) + value& assign( const Source& source ) { - value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc); - return *this; + value_ = boost::process::v2::detail::conv_string( + source.data(), source.data() + source.size()); + return *this; } template< class InputIt > @@ -759,25 +763,21 @@ struct value template< class CharT, class Traits = std::char_traits, class Alloc = std::allocator > std::basic_string - string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale()) const + basic_string( const Alloc& alloc = Alloc()) const { - return boost::process::v2::detail::convert_chars(value_.data(), value_.data() + value_.size(), CharT(), alloc, loc); + return boost::process::v2::detail::conv_string( + value_.data(), value_.size(),alloc); } - std::string string() const {return string();} - std::wstring wstring() const {return string();} - std::u16string u16string() const {return string();} - std::u32string u32string() const {return string();} + std::string string() const {return basic_string();} + std::wstring wstring() const {return basic_string();} + - std::basic_string> native_string() const + const string_type & native_string() const { - return string>(); + return value_; } -#if BOOST_PROCESS_HAS_CHAR8_T - std::u8string u8string() const {return string();} -#endif - bool empty() const {return value_.empty(); } friend bool operator==(const value & l, const value & r) { return l.value_ == r.value_; } @@ -791,7 +791,7 @@ struct value friend std::basic_ostream& operator<<( std::basic_ostream& os, const value& p ) { - os << boost::process::v2::quoted(p.string()); + os << boost::process::v2::quoted(p.basic_string()); return os; } @@ -889,7 +889,8 @@ struct key_value_pair key_value_pair() noexcept = default; key_value_pair( const key_value_pair& p ) = default; key_value_pair( key_value_pair&& p ) noexcept = default; - key_value_pair(key_view key, value_view value) : value_(key.string() + equality_sign + value.string()) {} + key_value_pair(key_view key, value_view value) : value_(key.basic_string() + equality_sign + + value.basic_string()) {} key_value_pair(key_view key, std::initializer_list> values) { @@ -915,29 +916,31 @@ struct key_value_pair explicit key_value_pair(key_value_pair_view kv) : value_(kv.c_str()) {} template< class Source > - key_value_pair( const Source& source, const std::locale& loc = std::locale(), - decltype(source.data()) = nullptr) - : value_(boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc)) + key_value_pair( const Source& source, + decltype(source.data()) = nullptr, + decltype(source.size()) = 0u) + : value_(boost::process::v2::detail::conv_string( + source.data(), source.size())) { } - key_value_pair(const typename conditional::value, wchar_t, char>::type * raw, const std::locale& loc = std::locale()) - : value_(boost::process::v2::detail::convert_chars( + key_value_pair(const typename conditional::value, wchar_t, char>::type * raw) + : value_(boost::process::v2::detail::conv_string( raw, - raw + std::char_traits::type>::type>::length(raw), - char_type(), std::allocator(), loc)) + std::char_traits::type>::type>::length(raw))) + { + } + key_value_pair(const typename conditional::value, wchar_t, char>::type * raw, + const std::locale& loc) + : value_(boost::process::v2::detail::conv_string( + raw, + std::char_traits::type>::type>::length(raw))) { } - - key_value_pair(const char16_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} - key_value_pair(const char32_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} -#if BOOST_PROCESS_HAS_CHAR8_T - key_value_pair(const char8_t * raw, const std::locale& loc = std::locale()) : value_(boost::process::v2::detail::convert_chars(raw,raw + std::char_traits::length(raw), char_type(), std::allocator(), loc)) {} -#endif template< class InputIt , typename std::iterator_traits::iterator_category> - key_value_pair( InputIt first, InputIt last, const std::locale& loc = std::locale()) - : key_value_pair(std::basic_string(first, last), loc) + key_value_pair( InputIt first, InputIt last ) + : key_value_pair(std::basic_string(first, last)) { } @@ -953,7 +956,8 @@ struct key_value_pair template< class Source > key_value_pair& operator=( const Source& source ) { - value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator()); + value_ = boost::process::v2::detail::conv_string( + source.data(), source.size()); return *this; } @@ -963,18 +967,26 @@ struct key_value_pair return *this; } template< class Source > - key_value_pair& assign( const Source& source, const std::locale & loc = std::locale() ) + key_value_pair& assign( const Source& source ) { - value_ = boost::process::v2::detail::convert_chars(source.data(), source.data() + source.size(), char_type(), std::allocator(), loc); + value_ = boost::process::v2::detail::conv_string( + source.data(), source.size()); return *this; } + template< class InputIt > key_value_pair& assign( InputIt first, InputIt last ) { return assign(std::string(first, last)); } + template< class InputIt > + key_value_pair& assign( InputIt first, InputIt last, const std::locale & loc ) + { + return assign(std::string(first, last), loc); + } + void clear() {value_.clear();} void swap( key_value_pair& other ) noexcept @@ -988,57 +1000,80 @@ struct key_value_pair operator string_type() const {return native();} operator string_view_type() const {return native_view();} + operator key_value_pair_view() const {return native_view();} - int compare( const key_value_pair& p ) const noexcept {return value_.compare(p.value_);} - int compare( const string_type& str ) const {return value_.compare(str);} - int compare( string_view_type str ) const {return -str.compare(value_);} - int compare( const value_type* s ) const {return value_.compare(s);} + int compare( const key_value_pair& p ) const noexcept + { + return key_value_pair_view(*this).compare(key_value_pair_view(p)); + } + + int compare( const string_type& str ) const + { + return key_value_pair_view(*this).compare(str); + } + int compare( string_view_type str ) const + { + return key_value_pair_view(*this).compare(str); + } + int compare( const value_type* s ) const + { + return key_value_pair_view(*this).compare(s); + } template< class CharT, class Traits = std::char_traits, class Alloc = std::allocator > std::basic_string - string( const Alloc& alloc = Alloc(), const std::locale & loc = std::locale() ) const + basic_string( const Alloc& alloc = Alloc() ) const { - return boost::process::v2::detail::convert_chars(value_.data(), value_.data() + value_.size(), CharT(), alloc, loc); + return boost::process::v2::detail::conv_string(value_.data(), value_.size(), alloc); } - std::string string() const {return string();} - std::wstring wstring() const {return string();} - std::u16string u16string() const {return string();} - std::u32string u32string() const {return string();} + std::string string() const {return basic_string();} + std::wstring wstring() const {return basic_string();} - std::basic_string> native_string() const + const string_type & native_string() const { - return string>(); + return value_; } - friend bool operator==(const key_value_pair & l, const key_value_pair & r) { return l.value_ == r.value_; } - friend bool operator!=(const key_value_pair & l, const key_value_pair & r) { return l.value_ != r.value_; } - friend bool operator<=(const key_value_pair & l, const key_value_pair & r) { return l.value_ <= r.value_; } - friend bool operator>=(const key_value_pair & l, const key_value_pair & r) { return l.value_ >= r.value_; } - friend bool operator< (const key_value_pair & l, const key_value_pair & r) { return l.value_ < r.value_; } - friend bool operator> (const key_value_pair & l, const key_value_pair & r) { return l.value_ > r.value_; } - -#if BOOST_PROCESS_HAS_CHAR8_T - std::u8string u8string() const {return string();} -#endif + friend bool operator==(const key_value_pair & l, const key_value_pair & r) { return l.compare(r) == 0; } + friend bool operator!=(const key_value_pair & l, const key_value_pair & r) { return l.compare(r) != 0; } + friend bool operator<=(const key_value_pair & l, const key_value_pair & r) { return l.compare(r) <= 0; } + friend bool operator>=(const key_value_pair & l, const key_value_pair & r) { return l.compare(r) >= 0; } + friend bool operator< (const key_value_pair & l, const key_value_pair & r) { return l.compare(r) < 0; } + friend bool operator> (const key_value_pair & l, const key_value_pair & r) { return l.compare(r) > 0; } bool empty() const {return value_.empty(); } struct key_view key() const { - const auto k = native_view().substr(0, value_.find(equality_sign)); + auto eq = value_.find(equality_sign); + if (eq == 0) + { + auto eq2 = value_.find(equality_sign, 1); + if (eq2 != string_type::npos) + eq = eq2; + } + const auto k = native_view().substr(0, eq); + return boost::process::v2::environment::key_view::string_view_type (k.data(), k.size()); } struct value_view value() const { - return value_view::string_view_type(native_view().substr(value_.find(equality_sign) + 1)); + auto eq = value_.find(equality_sign); + if (eq == 0) + { + auto eq2 = value_.find(equality_sign, 1); + if (eq2 != string_type::npos) + eq = eq2; + } + return value_view::string_view_type(native_view().substr(eq + 1)); } template< class CharT, class Traits > friend std::basic_ostream& operator<<( std::basic_ostream& os, const key_value_pair& p ) { - os << boost::process::v2::quoted(p.string()); + os << boost::process::v2::quoted(p.basic_string()); return os; } @@ -1060,14 +1095,14 @@ struct key_value_pair } template - inline auto get() const -> typename conditional::type; + inline auto get() const + -> typename conditional::type; const value_type * data() const {return value_.data(); } std::size_t size() const {return value_.size(); } private: - string_type value_; }; @@ -1146,13 +1181,13 @@ inline value_view key_value_pair::get<1u>() const return value(); } -struct view +struct current_view { using native_handle_type = environment::native_handle_type; using value_type = key_value_pair_view; - view() = default; - view(view && nt) = default; + current_view() = default; + current_view(current_view && nt) = default; native_handle_type native_handle() { return handle_.get(); } @@ -1205,8 +1240,9 @@ struct view detail::native_handle_deleter> handle_{environment::detail::load_native_handle()}; }; +inline current_view current() {return current_view();} -template +template inline boost::process::v2::filesystem::path home(Environment && env = view()) { auto find_key = [&](key_view ky) -> value @@ -1230,10 +1266,9 @@ inline boost::process::v2::filesystem::path home(Environment && env = view()) #else return find_key(L"HOME"); #endif - } -template +template inline boost::process::v2::filesystem::path find_executable( boost::process::v2::filesystem::path name, Environment && env = view()) diff --git a/include/boost/process/v2/error.hpp b/include/boost/process/v2/error.hpp index 54451488f..ca3c4da38 100644 --- a/include/boost/process/v2/error.hpp +++ b/include/boost/process/v2/error.hpp @@ -12,9 +12,14 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace error { -extern BOOST_PROCESS_V2_DECL const error_category& get_codecvt_category(); -static const error_category& codecvt_category = error::get_codecvt_category(); +enum utf8_conv_error +{ + insufficient_buffer = 1, + invalid_character, +}; +extern BOOST_PROCESS_V2_DECL const error_category& get_utf8_category(); +static const error_category& utf8_category = get_utf8_category(); } BOOST_PROCESS_V2_END_NAMESPACE diff --git a/include/boost/process/v2/impl/error.ipp b/include/boost/process/v2/impl/error.ipp index d437539fc..4bd8ba791 100644 --- a/include/boost/process/v2/impl/error.ipp +++ b/include/boost/process/v2/impl/error.ipp @@ -17,34 +17,34 @@ namespace detail { // can be replaced with filesystem::codecvt_error_category in boost -struct codecvt_category : public error_category +struct utf8_category final : public error_category { - codecvt_category() : error_category(0xDAEDu) {} + utf8_category() : error_category(0xDAEDu) {} const char* name() const noexcept { - return "process.v2.codecvt"; + return "process.v2.utf8"; } std::string message(int value) const { - if (value == std::codecvt_base::ok) - return "conversion completed without error."; - else if (value == std::codecvt_base::partial) - return "not enough space in the output buffer or unexpected end of source buffer"; - else if (value == std::codecvt_base::error) - return "encountered a character that could not be converted"; - else if (value == std::codecvt_base::noconv) - return "this facet is non-converting, no output written"; - return "process.v2.codecvt error"; + switch (static_cast(value)) + { + case utf8_conv_error::insufficient_buffer: + return "A supplied buffer size was not large enough"; + case utf8_conv_error::invalid_character: + return "Invalid characters were found in a string."; + default: + return "process.v2.utf8 error"; + } } }; } // namespace detail -BOOST_PROCESS_V2_DECL const error_category& get_codecvt_category() +BOOST_PROCESS_V2_DECL const error_category& get_utf8_category() { - static detail::codecvt_category instance; + static detail::utf8_category instance; return instance; } diff --git a/include/boost/process/v2/src.hpp b/include/boost/process/v2/src.hpp index 2beecc2c1..290fc445c 100644 --- a/include/boost/process/v2/src.hpp +++ b/include/boost/process/v2/src.hpp @@ -15,9 +15,8 @@ #include #include -#include #include #include #include - +#include #endif //BOOST_PROCESS_V2_SRC_HPP diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index 114b14796..bf190974f 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -11,7 +11,9 @@ function(boost_process_v2_standalone_test name) add_test(NAME boost_process_v2_${name} COMMAND $ ) endfunction() -boost_process_v2_standalone_test(codecvt) +boost_process_v2_standalone_test(utf8) boost_process_v2_standalone_test(cstring_ref) boost_process_v2_standalone_test(pid) -boost_process_v2_standalone_test(environment) \ No newline at end of file +boost_process_v2_standalone_test(environment) + +add_library(boost_process_v2_header_test header_1.cpp header_2.cpp) \ No newline at end of file diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index f665cb27f..bfdb03e93 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -23,14 +23,16 @@ project : requirements linux:-lpthread NT,cw:ws2_32 NT,gcc:ws2_32 - BOOST_PROCESS_V2_SEPARATE_COMPILATION=1 + BOOST_PROCESS_V2_SEPARATE_COMPILATION=1 ; - import testing ; alias filesystem : /boost//filesystem ; +lib header_test : header_1.cpp header_2.cpp : + BOOST_PROCESS_V2_HEADER_ONLY=1 ; + lib test_impl : test_impl.cpp filesystem : BOOST_PROCESS_V2_SEPARATE_COMPILATION=1 static @@ -38,8 +40,7 @@ lib test_impl : test_impl.cpp filesystem : ; test-suite standalone : - [ run environment.cpp test_impl ] - [ run codecvt.cpp test_impl ] + [ run utf8.cpp test_impl ] [ run cstring_ref.cpp test_impl ] [ run environment.cpp test_impl ] [ run pid.cpp test_impl ] diff --git a/test/v2/codecvt.cpp b/test/v2/codecvt.cpp deleted file mode 100644 index 39700f254..000000000 --- a/test/v2/codecvt.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// 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) - -// -// detail/codecvt.cpp -// ~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) -// -// 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) -// - -// Disable autolinking for unit tests. -#if !defined(BOOST_ALL_NO_LIB) -#define BOOST_ALL_NO_LIB 1 -#endif // !defined(BOOST_ALL_NO_LIB) - -// Test that header file is self-contained. -#include - -#include - - -BOOST_AUTO_TEST_CASE(test_codecvt) -{ - auto end = [](const auto * c){return c + std::char_traits>::length(c);}; - - const char * in = "test-input"; - - const wchar_t * win_t = L"test-input"; - const char16_t * in16_t = u"test-input"; - const char32_t * in32_t = U"test-input"; - -#if defined(BOOST_PROCESS_HAS_CHAR8_T) - const char8_t in8_t[] = u8"test-input"; -#endif - - BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), ' ') == in); - BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), L' ') == win_t); - - BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), L' ') == win_t); - BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), ' ') == in); - - BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), u' ') == in16_t); - BOOST_CHECK(boost::process::v2::detail::convert_chars(in16_t, end(in16_t), ' ') == in); - - BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), U' ') == in32_t); - BOOST_CHECK(boost::process::v2::detail::convert_chars(in32_t, end(in32_t), ' ') == in); - - BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), u' ') == in16_t); - BOOST_CHECK(boost::process::v2::detail::convert_chars(in16_t, end(in16_t), L' ') == win_t); - - BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), U' ') == in32_t); - BOOST_CHECK(boost::process::v2::detail::convert_chars(in32_t, end(in32_t), L' ') == win_t); - -#if defined(BOOST_PROCESS_HAS_CHAR8_T) - BOOST_CHECK(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), u8' ') == in8_t); - - BOOST_CHECK(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), ' ') == in); - BOOST_CHECK(boost::process::v2::detail::convert_chars(in, end(in), u8' ') == in8_t); - - BOOST_CHECK(boost::process::v2::detail::convert_chars(in8_t, end(in8_t), L' ') == win_t); - BOOST_CHECK(boost::process::v2::detail::convert_chars(win_t, end(win_t), u8' ') == in8_t); -#endif - -} diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index 637a4f8d4..d2d57be75 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -20,16 +20,17 @@ namespace bpe = boost::process::v2::environment; BOOST_AUTO_TEST_CASE(environment) { - BOOST_CHECK_EQUAL(bp2::filesystem::current_path(), bpe::get("PWD")); +#if defined(BOOST_PROCESS_V2_WINDOWS) +#endif for (const auto & elem : bpe::get("PATH")) BOOST_CHECK(std::find(elem.begin(), elem.end(), bpe::delimiter) == elem.end()); BOOST_CHECK(bpe::get("PATH").size() > 0); - const auto key1 = "BP2_TEST_NAME"; + const auto key1 = "BP2_TEST_NAME_\320\240\320\230\320\221\320\220"; // РИБА #if defined(BOOST_PROCESS_V2_WINDOWS) - const auto key2 = "BP2_TeSt_NamE"; + const auto key2 = "BP2_TeSt_NamE_\321\200\320\270\320\261\320\260"; // риба #else const auto key2 = key1; #endif @@ -37,20 +38,36 @@ BOOST_AUTO_TEST_CASE(environment) BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); bp2::error_code ec; bpe::get(key2, ec); - BOOST_CHECK_EQUAL(ec, boost::system::errc::permission_denied); + BOOST_CHECK(ec); + ec.clear(); bpe::set(key1, "some test string"); BOOST_CHECK(bpe::get(key1) == "some test string"); + + bpe::set(key2, "some test string"); +#if defined(BOOST_PROCESS_V2_POSIX) + //bpe::unset(key1); +#endif + + bpe::get(key1, ec); + BOOST_CHECK(!ec); + BOOST_CHECK(bpe::get(key2) == "some test string"); bpe::unset(key2); BOOST_CHECK_THROW(bpe::set("invalid=", "blablubb") , bp2::system_error); BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); bpe::get(key2, ec); - BOOST_CHECK_EQUAL(ec, boost::system::errc::invalid_argument); + BOOST_CHECK(ec); + ec.clear(); + + for (auto && ke : bpe::current()) + { + std::wcerr << "KV 1 " << ke << std::endl; + std::wcerr << "KV 2 " << ke.c_str() << std::endl; + BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); + } - for (const auto ke : bpe::view()) - BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); #if defined(BOOST_PROCESS_V2_POSIX) BOOST_CHECK_EQUAL(bpe::key("FOO"), bpe::key_view("FOO")); @@ -69,16 +86,14 @@ BOOST_AUTO_TEST_CASE(environment) BOOST_AUTO_TEST_CASE(wenvironment) { - BOOST_CHECK_EQUAL(bp2::filesystem::current_path(), bpe::get(L"PWD")); - for (const auto & elem : bpe::get(L"PATH")) BOOST_CHECK(std::find(elem.begin(), elem.end(), bpe::delimiter) == elem.end()); BOOST_CHECK(bpe::get(L"PATH").size() > 0); - const auto key1 = L"BP2_TEST_NAME"; + const auto key1 = L"BP2_TEST_NAME_W_\u0420\u0418\u0411\u0410"; #if defined(BOOST_PROCESS_V2_WINDOWS) - const auto key2 = L"BP2_TeSt_NamE"; + const auto key2 = L"BP2_TeSt_NamE_W_\u0440\u0438\u0431\u0430"; #else const auto key2 = key1; #endif @@ -86,7 +101,7 @@ BOOST_AUTO_TEST_CASE(wenvironment) BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); bp2::error_code ec; bpe::get(key2, ec); - BOOST_CHECK_EQUAL(ec, boost::system::errc::permission_denied); + BOOST_CHECK(ec); bpe::set(key1, L"some test string"); BOOST_CHECK(bpe::get(key1) == L"some test string"); @@ -96,21 +111,27 @@ BOOST_AUTO_TEST_CASE(wenvironment) BOOST_CHECK_THROW(bpe::set(L"invalid=", L"blablubb") , bp2::system_error); BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); bpe::get(key2, ec); - BOOST_CHECK_EQUAL(ec, boost::system::errc::invalid_argument); + BOOST_CHECK(ec); - for (const auto ke : bpe::view()) + for (const auto ke : bpe::current()) BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); + BOOST_CHECK(BOOST_PROCESS_V2_WINDOWS); #if defined(BOOST_PROCESS_V2_WINDOWS) - BOOST_CHECK_EQUAL(bpe::key(L"FOO"), bpe::key_view(L"FOO")); - BOOST_CHECK_EQUAL(bpe::key(L"FOO"), std::wstring(L"FOO")); - BOOST_CHECK_EQUAL(bpe::key_value_pair(L"FOO=BAR"), bpe::key_value_pair_view(L"FOO=BAR")); - BOOST_CHECK_EQUAL(bpe::key_value_pair(L"FOO", L"BAR"), bpe::key_value_pair_view(L"FOO=BAR")); + BOOST_CHECK_EQUAL(bpe::key(L"FOO"), bpe::key_view(L"Foo")); + BOOST_CHECK(bpe::key(L"FOO") == std::wstring(L"Foo")); + BOOST_CHECK_EQUAL(bpe::key_value_pair(L"Foo=BAR"), bpe::key_value_pair_view(L"FOO=BAR")); + BOOST_CHECK_EQUAL(bpe::key_value_pair(L"Foo=BAR"), bpe::key_value_pair(L"FOO=BAR")); + BOOST_CHECK_EQUAL(bpe::key_value_pair_view(L"Foo=BAR"), bpe::key_value_pair_view(L"FOO=BAR")); + BOOST_CHECK_EQUAL(bpe::key_value_pair(L"Foo", L"BAR"), bpe::key_value_pair_view(L"FOO=BAR")); - using sv = bpe::value::string_type; - std::string cmp = sv(L"FOO=X") + bpe::delimiter + sv(L"YY") + bpe::delimiter + sv(L"Z42"); - BOOST_CHECK_EQUAL(bpe::key_value_pair(L"FOO", {L"X", L"YY", L"Z42"}), cmp); + BOOST_CHECK_NE(bpe::key_value_pair(L"FOO=BAR"), bpe::key_value_pair_view(L"FOO=Bar")); + BOOST_CHECK_LT(bpe::key_value_pair(L"FOO=BAR"), bpe::key_value_pair_view(L"goo=Bar")); + BOOST_CHECK_NE(bpe::key_value_pair(L"FOO", L"BAR"), bpe::key_value_pair_view(L"FOO=Bar")); + using sv = bpe::value::string_type; + std::wstring cmp = sv(L"FOO=X") + bpe::delimiter + sv(L"YY") + bpe::delimiter + sv(L"Z42"); + BOOST_CHECK(bpe::key_value_pair(L"FOO", {L"X", L"YY", L"Z42"}) == cmp); #endif } diff --git a/test/v2/header_1.cpp b/test/v2/header_1.cpp new file mode 100644 index 000000000..1ed397ce4 --- /dev/null +++ b/test/v2/header_1.cpp @@ -0,0 +1,6 @@ +// 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) + +#include \ No newline at end of file diff --git a/test/v2/header_2.cpp b/test/v2/header_2.cpp new file mode 100644 index 000000000..1ed397ce4 --- /dev/null +++ b/test/v2/header_2.cpp @@ -0,0 +1,6 @@ +// 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) + +#include \ No newline at end of file From 27f79e1774bcb4216650313038b4526909c3002b Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Thu, 26 May 2022 15:54:21 +0800 Subject: [PATCH 053/397] Added missing files. --- include/boost/process/v2/detail/impl/utf8.ipp | 114 ++++++++++++++++++ test/v2/utf8.cpp | 39 ++++++ 2 files changed, 153 insertions(+) create mode 100644 include/boost/process/v2/detail/impl/utf8.ipp create mode 100644 test/v2/utf8.cpp diff --git a/include/boost/process/v2/detail/impl/utf8.ipp b/include/boost/process/v2/detail/impl/utf8.ipp new file mode 100644 index 000000000..197f1dc99 --- /dev/null +++ b/include/boost/process/v2/detail/impl/utf8.ipp @@ -0,0 +1,114 @@ +// 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_DETAIL_IMPL_UTF8_HPP +#define BOOST_PROCESS_V2_DETAIL_IMPL_UTF8_HPP + +#include +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +inline void handle_error(error_code & ec) +{ + const auto err = ::GetLastError(); + switch (err) + { + case ERROR_INSUFFICIENT_BUFFER: + ec.assign(error::insufficient_buffer, error::utf8_category); + break; + case ERROR_NO_UNICODE_TRANSLATION: + ec.assign(error::invalid_character, error::utf8_category); + break; + default: + ec.assign(err, system_category()); + } +} + +std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec) +{ + auto res = WideCharToMultiByte( + CP_UTF8, // CodePage, + 0, // dwFlags, + in, // lpWideCharStr, + static_cast(size), // cchWideChar, + nullptr, // lpMultiByteStr, + 0, // cbMultiByte, + nullptr, // lpDefaultChar, + FALSE); // lpUsedDefaultChar + + if (res == 0u) + handle_error(ec); + return static_cast(res); +} + +std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec) +{ + auto res = ::MultiByteToWideChar( + CP_UTF8, // CodePage + 0, // dwFlags + in, // lpMultiByteStr + static_cast(size), // cbMultiByte + nullptr, // lpWideCharStr + 0); // cchWideChar + if (res == 0u) + handle_error(ec); + + return static_cast(res); +} + +std::size_t convert_to_utf8(const wchar_t *in, std::size_t size, char * out, + std::size_t max_size, error_code & ec) +{ + auto res = ::WideCharToMultiByte( + CP_UTF8, // CodePage + 0, // dwFlags + in, // lpWideCharStr + static_cast(size), // cchWideChar + out, // lpMultiByteStr + static_cast(max_size), // cbMultiByte + nullptr, // lpDefaultChar + FALSE); // lpUsedDefaultChar + if (res == 0u) + handle_error(ec); + + return static_cast(res); +} + +std::size_t convert_to_wide(const char *in, std::size_t size, wchar_t * out, + std::size_t max_size, error_code & ec) +{ + auto res = ::MultiByteToWideChar( + CP_UTF8, // CodePage + 0, // dwFlags + in, // lpMultiByteStr + static_cast(size), // cbMultiByte + out, // lpWideCharStr + static_cast(max_size)); // cchWideChar + if (res == 0u) + handle_error(ec); + + return static_cast(res); +} + +#else + +#endif + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_DETAIL_IMPL_UTF8_HPP diff --git a/test/v2/utf8.cpp b/test/v2/utf8.cpp new file mode 100644 index 000000000..6383887d7 --- /dev/null +++ b/test/v2/utf8.cpp @@ -0,0 +1,39 @@ +// 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) + +// +// detail/codecvt.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include + +#include + +BOOST_AUTO_TEST_CASE(test_codecvt) +{ + auto end = [](const auto * c){return std::char_traits>::length(c);}; + + const char * in = "test-input-\320\240\320\230\320\221\320\220"; + const wchar_t * win_t = L"test-input-\u0420\u0418\u0411\u0410"; + + BOOST_CHECK(boost::process::v2::detail::conv_string ( in, end( in )) == in ); + BOOST_CHECK(boost::process::v2::detail::conv_string(win_t, end(win_t)) == win_t); + + BOOST_CHECK(boost::process::v2::detail::conv_string( in, end( in )) == win_t); + BOOST_CHECK(boost::process::v2::detail::conv_string (win_t, end(win_t)) == in ); + +} From 1b61ba6ea7712fc25158d0342690b0c862817f55 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Thu, 26 May 2022 15:59:49 +0800 Subject: [PATCH 054/397] Fixed InputIt overlaods. --- include/boost/process/v2/environment.hpp | 26 +++++++----------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index f6119d9eb..d86c52396 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -465,13 +465,6 @@ struct key explicit key(key_view kv) : value_(kv.native_string()) {} - template< class Source > - key( const Source& source, const std::locale& loc, - decltype(source.data()) = nullptr) - : value_( - boost::process::v2::detail::conv_string(source.data(), source.size())) - { - } template< class Source > key( const Source& source, @@ -491,7 +484,7 @@ struct key template< class InputIt > key( InputIt first, InputIt last) - : key(std::basic_string(first, last)) + : key(std::basic_string>::value_type>(first, last)) { } @@ -526,7 +519,8 @@ struct key template< class InputIt > key& assign( InputIt first, InputIt last ) { - return assign(std::string(first, last)); + + return assign(std::basic_string>::value_type>(first, last)); } void clear() {value_.clear();} @@ -695,7 +689,7 @@ struct value template< class InputIt > value( InputIt first, InputIt last) - : value(std::basic_string(first, last)) + : value(std::basic_string>::value_type>(first, last)) { } @@ -732,7 +726,7 @@ struct value template< class InputIt > value& assign( InputIt first, InputIt last ) { - return assign(std::string(first, last)); + return assign(std::basic_string>::value_type>(first, last)); } void push_back(const value & sv) @@ -940,7 +934,7 @@ struct key_value_pair template< class InputIt , typename std::iterator_traits::iterator_category> key_value_pair( InputIt first, InputIt last ) - : key_value_pair(std::basic_string(first, last)) + : key_value_pair(std::basic_string>::value_type>(first, last)) { } @@ -978,13 +972,7 @@ struct key_value_pair template< class InputIt > key_value_pair& assign( InputIt first, InputIt last ) { - return assign(std::string(first, last)); - } - - template< class InputIt > - key_value_pair& assign( InputIt first, InputIt last, const std::locale & loc ) - { - return assign(std::string(first, last), loc); + return assign(std::basic_string>::value_type>(first, last)); } void clear() {value_.clear();} From a46ab25046845ce0dda2401385c963b2fd18c1c9 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 26 May 2022 16:58:20 +0800 Subject: [PATCH 055/397] Added utf8 on linux. --- include/boost/process/v2/detail/impl/utf8.ipp | 265 ++++++++++++++++++ include/boost/process/v2/environment.hpp | 8 +- test/v2/environment.cpp | 1 - test/v2/utf8.cpp | 10 - 4 files changed, 269 insertions(+), 15 deletions(-) mode change 100644 => 100755 include/boost/process/v2/detail/impl/utf8.ipp mode change 100644 => 100755 test/v2/utf8.cpp diff --git a/include/boost/process/v2/detail/impl/utf8.ipp b/include/boost/process/v2/detail/impl/utf8.ipp old mode 100644 new mode 100755 index 197f1dc99..22623df69 --- a/include/boost/process/v2/detail/impl/utf8.ipp +++ b/include/boost/process/v2/detail/impl/utf8.ipp @@ -105,6 +105,271 @@ std::size_t convert_to_wide(const char *in, std::size_t size, wchar_t * out, #else + +template +inline int get_cont_octet_out_count_impl(wchar_t word) { + if (word < 0x80) { + return 0; + } + if (word < 0x800) { + return 1; + } + return 2; +} + +template<> +inline int get_cont_octet_out_count_impl<4>(wchar_t word) { + if (word < 0x80) { + return 0; + } + if (word < 0x800) { + return 1; + } + + // Note that the following code will generate warnings on some platforms + // where wchar_t is defined as UCS2. The warnings are superfluous as the + // specialization is never instantitiated with such compilers, but this + // can cause problems if warnings are being treated as errors, so we guard + // against that. Including as we do + // should be enough to get WCHAR_MAX defined. +#if !defined(WCHAR_MAX) +# error WCHAR_MAX not defined! +#endif + // cope with VC++ 7.1 or earlier having invalid WCHAR_MAX +#if defined(_MSC_VER) && _MSC_VER <= 1310 // 7.1 or earlier + return 2; +#elif WCHAR_MAX > 0x10000 + + if (word < 0x10000) { + return 2; + } + if (word < 0x200000) { + return 3; + } + if (word < 0x4000000) { + return 4; + } + return 5; + +#else + return 2; +#endif +} + +inline int get_cont_octet_out_count(wchar_t word) +{ + return detail::get_cont_octet_out_count_impl(word); +} + +// copied from boost/detail/utf8_codecvt_facet.ipp +// Copyright (c) 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu) +// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu). + +inline unsigned int get_octet_count(unsigned char lead_octet) +{ + // if the 0-bit (MSB) is 0, then 1 character + if (lead_octet <= 0x7f) return 1; + + // Otherwise the count number of consecutive 1 bits starting at MSB +// assert(0xc0 <= lead_octet && lead_octet <= 0xfd); + + if (0xc0 <= lead_octet && lead_octet <= 0xdf) return 2; + else if (0xe0 <= lead_octet && lead_octet <= 0xef) return 3; + else if (0xf0 <= lead_octet && lead_octet <= 0xf7) return 4; + else if (0xf8 <= lead_octet && lead_octet <= 0xfb) return 5; + else return 6; +} + +inline bool invalid_continuing_octet(unsigned char octet_1) { + return (octet_1 < 0x80|| 0xbf< octet_1); +} + +inline unsigned int get_cont_octet_count(unsigned char lead_octet) +{ + return get_octet_count(lead_octet) - 1; +} + +inline const wchar_t * get_octet1_modifier_table() noexcept +{ + static const wchar_t octet1_modifier_table[] = { + 0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc + }; + return octet1_modifier_table; +} + + +std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec) +{ + std::size_t res = 0u; + const auto from_end = in + size; + for (auto from = in; from != from_end; from++) + res += get_cont_octet_out_count(*from) + 1; + return res; +} + +std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec) +{ + const auto from = in; + const auto from_end = from + size; + const char * from_next = from; + for (std::size_t char_count = 0u; from_next < from_end; ++char_count) { + unsigned int octet_count = get_octet_count(*from_next); + // The buffer may represent incomplete characters, so terminate early if one is found + if (octet_count > static_cast(from_end - from_next)) + break; + from_next += octet_count; + } + + return from_next - from; +} + +std::size_t convert_to_utf8(const wchar_t * in, std::size_t size, + char * out, std::size_t max_size, error_code & ec) +{ + + const wchar_t * from = in; + const wchar_t * from_end = from + size; + const wchar_t * & from_next = from; + char * to = out; + char * to_end = out + max_size; + char * & to_next = to; + + const wchar_t * const octet1_modifier_table = get_octet1_modifier_table(); + wchar_t max_wchar = (std::numeric_limits::max)(); + while (from != from_end && to != to_end) { + + // Check for invalid UCS-4 character + if (*from > max_wchar) { + from_next = from; + to_next = to; + ec.assign(error::invalid_character, error::get_utf8_category()); + return 0u; + } + + int cont_octet_count = get_cont_octet_out_count(*from); + + // RG - comment this formula better + int shift_exponent = cont_octet_count * 6; + + // Process the first character + *to++ = static_cast(octet1_modifier_table[cont_octet_count] + + (unsigned char)(*from / (1 << shift_exponent))); + + // Process the continuation characters + // Invariants: At the start of the loop: + // 1) 'i' continuing octets have been generated + // 2) '*to' points to the next location to place an octet + // 3) shift_exponent is 6 more than needed for the next octet + int i = 0; + while (i != cont_octet_count && to != to_end) { + shift_exponent -= 6; + *to++ = static_cast(0x80 + ((*from / (1 << shift_exponent)) % (1 << 6))); + ++i; + } + // If we filled up the out buffer before encoding the character + if (to == to_end && i != cont_octet_count) { + from_next = from; + to_next = to - (i + 1); + ec.assign(error::insufficient_buffer, error::get_utf8_category()); + return 0u; + } + ++from; + } + from_next = from; + to_next = to; + + // Were we done or did we run out of destination space + if (from != from_end) + ec.assign(error::insufficient_buffer, error::get_utf8_category()); + + return to_next - out; +} + +inline bool invalid_leading_octet(unsigned char octet_1) { + return (0x7f < octet_1 && octet_1 < 0xc0) || + (octet_1 > 0xfd); +} + +std::size_t convert_to_wide(const char * in, std::size_t size, + wchar_t * out, std::size_t max_size, error_code & ec) +{ + const char * from = in; + const char * from_end = from + size; + const char * & from_next = from; + wchar_t * to = out; + wchar_t * to_end = out + max_size; + wchar_t * & to_next = to; + + // Basic algorithm: The first octet determines how many + // octets total make up the UCS-4 character. The remaining + // "continuing octets" all begin with "10". To convert, subtract + // the amount that specifies the number of octets from the first + // octet. Subtract 0x80 (1000 0000) from each continuing octet, + // then mash the whole lot together. Note that each continuing + // octet only uses 6 bits as unique values, so only shift by + // multiples of 6 to combine. + const wchar_t * const octet1_modifier_table = detail::get_octet1_modifier_table(); + while (from != from_end && to != to_end) { + + // Error checking on the first octet + if (invalid_leading_octet(*from)) { + from_next = from; + to_next = to; + ec.assign(error::invalid_character, error::get_utf8_category()); + return 0u; + } + + // The first octet is adjusted by a value dependent upon + // the number of "continuing octets" encoding the character + const int cont_octet_count = get_cont_octet_count(*from); + + // The unsigned char conversion is necessary in case char is + // signed (I learned this the hard way) + wchar_t ucs_result = + (unsigned char)(*from++) - octet1_modifier_table[cont_octet_count]; + + // Invariants: + // 1) At the start of the loop, 'i' continuing characters have been + // processed + // 2) *from points to the next continuing character to be processed. + int i = 0; + while (i != cont_octet_count && from != from_end) { + + // Error checking on continuing characters + if (invalid_continuing_octet(*from)) { + from_next = from; + to_next = to; + ec.assign(error::invalid_character, error::get_utf8_category()); + return 0u; + } + + ucs_result *= (1 << 6); + + // each continuing character has an extra (10xxxxxx)b attached to + // it that must be removed. + ucs_result += (unsigned char)(*from++) - 0x80; + ++i; + } + + // If the buffer ends with an incomplete unicode character... + if (from == from_end && i != cont_octet_count) { + // rewind "from" to before the current character translation + from_next = from - (i + 1); + to_next = to; + ec.assign(error::insufficient_buffer, error::get_utf8_category()); + return 0u; + } + *to++ = ucs_result; + } + from_next = from; + to_next = to; + + if (from != from_end) + ec.assign(error::insufficient_buffer, error::get_utf8_category()); + + return to_next - out; +} + #endif } diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index d86c52396..5932482e7 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -677,7 +677,7 @@ struct value decltype(source.data()) = nullptr, decltype(source.size()) = 0u) : value_(boost::process::v2::detail::conv_string( - source.data(), source.data() + source.size())) + source.data(), source.size())) { } @@ -719,7 +719,7 @@ struct value value& assign( const Source& source ) { value_ = boost::process::v2::detail::conv_string( - source.data(), source.data() + source.size()); + source.data(), source.size()); return *this; } @@ -1231,7 +1231,7 @@ struct current_view inline current_view current() {return current_view();} template -inline boost::process::v2::filesystem::path home(Environment && env = view()) +inline boost::process::v2::filesystem::path home(Environment && env = current()) { auto find_key = [&](key_view ky) -> value { @@ -1259,7 +1259,7 @@ inline boost::process::v2::filesystem::path home(Environment && env = view()) template inline boost::process::v2::filesystem::path find_executable( boost::process::v2::filesystem::path name, - Environment && env = view()) + Environment && env = current()) { auto find_key = [&](key_view ky) -> value_view { diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index d2d57be75..ad0a3a797 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -116,7 +116,6 @@ BOOST_AUTO_TEST_CASE(wenvironment) for (const auto ke : bpe::current()) BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); - BOOST_CHECK(BOOST_PROCESS_V2_WINDOWS); #if defined(BOOST_PROCESS_V2_WINDOWS) BOOST_CHECK_EQUAL(bpe::key(L"FOO"), bpe::key_view(L"Foo")); BOOST_CHECK(bpe::key(L"FOO") == std::wstring(L"Foo")); diff --git a/test/v2/utf8.cpp b/test/v2/utf8.cpp old mode 100644 new mode 100755 index 6383887d7..8922ba13b --- a/test/v2/utf8.cpp +++ b/test/v2/utf8.cpp @@ -3,16 +3,6 @@ // 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) -// -// detail/codecvt.cpp -// ~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) -// -// 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) -// - // Disable autolinking for unit tests. #if !defined(BOOST_ALL_NO_LIB) #define BOOST_ALL_NO_LIB 1 From f93290d3d4d6c363d14ec878061d6174eddbbe44 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sun, 29 May 2022 02:38:37 +0800 Subject: [PATCH 056/397] Completed windows port to v2 from asio. --- include/boost/process/v2.hpp | 4 + include/boost/process/v2/default_launcher.hpp | 36 ++ include/boost/process/v2/detail/config.hpp | 15 + .../v2/detail/impl/process_handle_windows.ipp | 124 ++++++ .../v2/detail/process_handle_windows.hpp | 243 +++++++++++ .../boost/process/v2/detail/throw_error.hpp | 6 + include/boost/process/v2/environment.hpp | 146 ++++++- include/boost/process/v2/exit_code.hpp | 70 ++++ .../process/v2/impl/default_launcher.ipp | 22 + include/boost/process/v2/impl/environment.ipp | 49 +++ .../boost/process/v2/impl/process_handle.ipp | 17 + include/boost/process/v2/process.hpp | 335 +++++++++++++++ include/boost/process/v2/process_handle.hpp | 25 ++ include/boost/process/v2/src.hpp | 4 + include/boost/process/v2/start_dir.hpp | 48 +++ include/boost/process/v2/stdio.hpp | 187 +++++++++ .../process/v2/windows/as_user_launcher.hpp | 137 +++++++ .../process/v2/windows/creation_flags.hpp | 39 ++ .../process/v2/windows/default_launcher.hpp | 380 ++++++++++++++++++ .../v2/windows/impl/default_launcher.ipp | 80 ++++ .../boost/process/v2/windows/show_window.hpp | 52 +++ .../v2/windows/with_logon_launcher.hpp | 151 +++++++ .../v2/windows/with_token_launcher.hpp | 140 +++++++ test/v2/CMakeLists.txt | 1 + test/v2/Jamfile.jam | 9 + test/v2/cstring_ref.cpp | 2 +- test/v2/environment.cpp | 1 - test/v2/pid.cpp | 2 +- test/v2/process.cpp | 63 +++ test/v2/target.cpp | 11 + test/v2/test_impl.cpp | 3 +- test/v2/utf8.cpp | 2 +- 32 files changed, 2397 insertions(+), 7 deletions(-) create mode 100644 include/boost/process/v2/default_launcher.hpp create mode 100644 include/boost/process/v2/detail/impl/process_handle_windows.ipp create mode 100644 include/boost/process/v2/detail/process_handle_windows.hpp create mode 100644 include/boost/process/v2/exit_code.hpp create mode 100644 include/boost/process/v2/impl/default_launcher.ipp create mode 100644 include/boost/process/v2/impl/environment.ipp create mode 100644 include/boost/process/v2/impl/process_handle.ipp create mode 100644 include/boost/process/v2/process.hpp create mode 100644 include/boost/process/v2/process_handle.hpp create mode 100644 include/boost/process/v2/start_dir.hpp create mode 100644 include/boost/process/v2/stdio.hpp create mode 100644 include/boost/process/v2/windows/as_user_launcher.hpp create mode 100644 include/boost/process/v2/windows/creation_flags.hpp create mode 100644 include/boost/process/v2/windows/default_launcher.hpp create mode 100644 include/boost/process/v2/windows/impl/default_launcher.ipp create mode 100644 include/boost/process/v2/windows/show_window.hpp create mode 100644 include/boost/process/v2/windows/with_logon_launcher.hpp create mode 100644 include/boost/process/v2/windows/with_token_launcher.hpp create mode 100644 test/v2/process.cpp create mode 100644 test/v2/target.cpp diff --git a/include/boost/process/v2.hpp b/include/boost/process/v2.hpp index bdb995c4b..ad5f2d059 100644 --- a/include/boost/process/v2.hpp +++ b/include/boost/process/v2.hpp @@ -7,6 +7,10 @@ #include #include +#include #include +#include +#include +#include #endif //BOOST_PROCESS_V2_HPP diff --git a/include/boost/process/v2/default_launcher.hpp b/include/boost/process/v2/default_launcher.hpp new file mode 100644 index 000000000..3dee6ed73 --- /dev/null +++ b/include/boost/process/v2/default_launcher.hpp @@ -0,0 +1,36 @@ +// +// boost/process/v2/default_launcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_DEFAULT_LAUNCHER_HPP +#define BOOST_PROCESS_V2_DEFAULT_LAUNCHER_HPP + +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(BOOST_PROCESS_V2_WINDOWS) +typedef windows::default_launcher default_process_launcher; +#else + + +#endif + + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) +#include +#endif + +#endif //BOOST_PROCESS_V2_DEFAULT_LAUNCHER_HPP \ No newline at end of file diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp index c7d75e7df..59708cc58 100644 --- a/include/boost/process/v2/detail/config.hpp +++ b/include/boost/process/v2/detail/config.hpp @@ -7,6 +7,13 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) +#define BOOST_PROCESS_V2_ASIO_NAMESPACE ::asio +#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) ASIO_COMPLETION_TOKEN_FOR(Sig) +#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) +#define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature) +#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) ASIO_DEFAULT_COMPLETION_TOKEN(Executor) + + #include #include #include @@ -31,6 +38,14 @@ #define BOOST_PROCESS_V2_NAMESPACE process_v2 #else + +#define BOOST_PROCESS_V2_ASIO_NAMESPACE ::boost::asio +#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) BOOST_ASIO_COMPLETION_TOKEN_FOR(Sig) +#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) +#define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature) +#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor) + + #include #include #include diff --git a/include/boost/process/v2/detail/impl/process_handle_windows.ipp b/include/boost/process/v2/detail/impl/process_handle_windows.ipp new file mode 100644 index 000000000..293abb085 --- /dev/null +++ b/include/boost/process/v2/detail/impl/process_handle_windows.ipp @@ -0,0 +1,124 @@ +// 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_DETAIL_IMPL_PROCESS_HANDLE_WINDOWS_IPP +#define BOOST_PROCESS_V2_DETAIL_IMPL_PROCESS_HANDLE_WINDOWS_IPP + +#include +#include +#include +#include + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +void get_exit_code_( + HANDLE handle, + native_exit_code_type & exit_code, + error_code & ec) +{ + if (!::GetExitCodeProcess(handle, &exit_code)) + ec = detail::get_last_error(); +} + + +HANDLE open_process_(DWORD pid) +{ + auto proc = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, pid); + if (proc == nullptr) + detail::throw_last_error("open_process()"); + return proc; +} + + +void terminate_if_running_(HANDLE handle) +{ + DWORD exit_code = 0u; + if (handle == INVALID_HANDLE_VALUE) + return ; + if (::GetExitCodeProcess(handle, &exit_code)) + if (exit_code == STILL_ACTIVE) + ::TerminateProcess(handle, 260); +} + +bool check_handle_(HANDLE handle, error_code & ec) +{ + if (handle == INVALID_HANDLE_VALUE) + { + ec.assign(ERROR_INVALID_HANDLE_STATE, system_category()); + return false; + } + return true; +} + +bool check_pid_(pid_type pid_, error_code & ec) +{ + if (pid_ == 0) + { + ec.assign(ERROR_INVALID_HANDLE_STATE, system_category()); + return false; + } + return true; +} + +struct enum_windows_data_t +{ + error_code &ec; + pid_type pid; +}; + +static BOOL CALLBACK enum_window(HWND hwnd, LPARAM param) + { + auto data = reinterpret_cast(param); + DWORD pid{0u}; + GetWindowThreadProcessId(hwnd, &pid); + + if (pid != data->pid) + return TRUE; + + LRESULT res = ::SendMessageW(hwnd, WM_CLOSE, 0, 0); + if (!res) + data->ec = detail::get_last_error(); + return res != 0; + } + +void request_exit_(pid_type pid_, error_code & ec) +{ + enum_windows_data_t data{ec, pid_}; + + if (!::EnumWindows(enum_window, reinterpret_cast(&data))) + ec = detail::get_last_error(); +} + +void interrupt_(pid_type pid_, error_code & ec) +{ + if (!::GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid_)) + ec = detail::get_last_error(); +} + +void terminate_(HANDLE handle, error_code & ec, DWORD & exit_status) +{ + if (!::TerminateProcess(handle, 260)) + ec = detail::get_last_error(); +} + +void check_running_(HANDLE handle, error_code & ec, DWORD & exit_status) +{ + if (!::GetExitCodeProcess(handle, &exit_status)) + ec = detail::get_last_error(); +} + +} + +#if !defined(BOOST_PROCESS_V2_HEADER_ONLY) +template struct basic_process_handle<>; +#endif + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_DETAIL_IMPL_PROCESS_HANDLE_WINDOWS_IPP diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp new file mode 100644 index 000000000..3f3f87bae --- /dev/null +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -0,0 +1,243 @@ +// 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_DETAIL_PROCESS_HANDLE_WINDOWS_HPP +#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP + +#include + +#include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#include +#include +#else +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +BOOST_PROCESS_V2_DECL void get_exit_code_( void * handle, native_exit_code_type & exit_code, error_code & ec); +BOOST_PROCESS_V2_DECL void * open_process_(pid_type pid); +BOOST_PROCESS_V2_DECL void terminate_if_running_(void * handle); +BOOST_PROCESS_V2_DECL bool check_handle_(void* handle, error_code & ec); +BOOST_PROCESS_V2_DECL bool check_pid_(pid_type pid_, error_code & ec); +BOOST_PROCESS_V2_DECL void interrupt_(pid_type pid_, error_code & ec); +BOOST_PROCESS_V2_DECL void terminate_(void * handle, error_code & ec, native_exit_code_type & exit_code); +BOOST_PROCESS_V2_DECL void request_exit_(pid_type pid_, error_code & ec); +BOOST_PROCESS_V2_DECL void check_running_(void* handle, error_code & ec, native_exit_code_type & exit_status); + +} + +template +struct basic_process_handle +{ + typedef BOOST_PROCESS_V2_ASIO_NAMESPACE::windows::basic_object_handle handle_type; + typedef typename handle_type::native_handle_type native_handle_type; + + typedef Executor executor_type; + executor_type get_executor() {return handle_.get_executor();} + + /// Rebinds the process_handle to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_process_handle other; + }; + + template + basic_process_handle(ExecutionContext &context, + typename std::enable_if< + std::is_convertible::value + >::type = 0) + : pid_(0), handle_(context) + { + } + + basic_process_handle(Executor executor) + : pid_(0), handle_(executor) + { + } + + basic_process_handle(Executor executor, pid_type pid) + : pid_(pid), handle_(executor, detail::open_process_(pid)) + { + } + + basic_process_handle(Executor executor, pid_type pid, native_handle_type process_handle) + : pid_(pid), handle_(executor, process_handle) + { + } + + native_handle_type native_handle() {return handle_.native_handle();} + pid_type id() const {return pid_;} + + void terminate_if_running(error_code & ) + { + detail::terminate_if_running_(handle_.native_handle()); + } + + void terminate_if_running() + { + detail::terminate_if_running_(handle_.native_handle()); + } + + void wait(native_exit_code_type & exit_status, error_code & ec) + { + if (!detail::check_handle_(handle_.native_handle(), ec)) + return; + + handle_.wait(ec); + if (!ec) + detail::get_exit_code_(handle_.native_handle(), exit_status, ec); + } + + + void wait(native_exit_code_type & exit_status) + { + error_code ec; + wait(exit_status, ec); + if (ec) + detail::throw_error(ec, "wait(pid)"); + } + + void interrupt(error_code & ec) + { + if (!detail::check_pid_(pid_, ec)) + return; + + detail::interrupt_(pid_, ec); + } + + void interrupt() + { + error_code ec; + interrupt(ec); + if (ec) + detail::throw_error(ec, "interrupt"); + } + + void request_exit(error_code & ec) + { + if (!detail::check_pid_(pid_, ec)) + return ; + + detail::request_exit_(pid_, ec); + } + + void request_exit() + { + error_code ec; + request_exit(ec); + if (ec) + detail::throw_error(ec, "request_exit"); + } + + void terminate(native_exit_code_type & exit_status, error_code & ec) + { + if (!detail::check_handle_(handle_.native_handle(), ec)) + return; + + detail::terminate_(handle_.native_handle(), ec, exit_status); + if (!ec) + wait(exit_status, ec); + + } + + void terminate(native_exit_code_type & exit_status) + { + error_code ec; + terminate(exit_status, ec); + if (ec) + detail::throw_error(ec, "terminate"); + } + + bool running(native_exit_code_type & exit_code, error_code ec) + { + if (!detail::check_handle_(handle_.native_handle(), ec)) + return false; + + native_exit_code_type code; + //single value, not needed in the winapi. + detail::check_running_(handle_.native_handle(), ec, code); + if (ec) + return false; + + if (process_is_running(code)) + return true; + else + { + exit_code = code; + return false; + } + } + + bool running(native_exit_code_type & exit_code) + { + error_code ec; + bool res = running(exit_code, ec); + if (ec) + detail::throw_error(ec, "is_running"); + return res; + } + + bool is_open() const + { + return handle_.is_open(); + } + + template + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) + async_wait(WaitHandler && handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( + async_wait_op_{handle_}, handler, handle_ + ); + } + + private: + pid_type pid_; + handle_type handle_; + + struct async_wait_op_ + { + handle_type & handle; + + template + void operator()(Self && self) + { + handle.async_wait(std::move(self)); + } + + template + void operator()(Self && self, error_code ec) + { + native_exit_code_type exit_code; + if (!ec) + detail::get_exit_code_(handle.native_handle(), exit_code, ec); + std::move(self).complete(ec, exit_code); + } + }; +}; + +using process_handle = basic_process_handle<>; + +#if !defined(BOOST_PROCESS_V2_HEADER_ONLY) +extern template struct basic_process_handle<>; +#endif + +BOOST_PROCESS_V2_END_NAMESPACE + + +#endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP diff --git a/include/boost/process/v2/detail/throw_error.hpp b/include/boost/process/v2/detail/throw_error.hpp index 9d0d9867d..4ddb682ce 100644 --- a/include/boost/process/v2/detail/throw_error.hpp +++ b/include/boost/process/v2/detail/throw_error.hpp @@ -29,4 +29,10 @@ inline void throw_error(const error_code& err, const char* location) } BOOST_PROCESS_V2_END_NAMESPACE +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + #endif //BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index 5932482e7..718233dc7 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -1471,8 +1471,152 @@ struct tuple_element<1u, boost::process::v2::environment::key_value_pair_view> using type = boost::process::v2::environment::value_view; }; - } +// sub process environment stuff +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(BOOST_PROCESS_V2_WINDOWS) +namespace windows { struct default_launcher ;} +#else +namespace posix { struct default_launcher ;} +#endif + +struct process_environment +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) + + + template + void build_env(Args && args, string_view rs) + { + std::vector vec; + // vec.reserve(std::end(args) - std::begin(args)); + std::size_t length = 0u; + for (decltype(rs) v : std::forward(args)) + { + vec.push_back(v); + length += v.size() + 1u; + } + length ++ ; + + ascii_env.resize(length); + + auto itr = ascii_env.begin(); + for (const auto & v : vec ) + { + itr = std::copy(v.begin(), v.end(), itr); + *(itr++) = '\0'; + } + ascii_env.back() = '\0'; + } + template + void build_env(Args && args, wstring_view rs) + { + std::vector vec; +// vec.reserve(std::end(args) - std::begin(args)); + std::size_t length = 0u; + for (decltype(rs) v : std::forward(args)) + { + vec.push_back(v); + length += v.size() + 1u; + } + length ++ ; + + unicode_env.resize(length); + + auto itr = unicode_env.begin(); + for (const auto & v : vec ) + { + itr = std::copy(v.begin(), v.end(), itr); + *(itr++) = L'\0'; + } + unicode_env.back() = L'\0'; + } + + + process_environment(std::initializer_list sv) { build_env(sv, ""); } + process_environment(std::initializer_list sv) { build_env(sv, L""); } + + template + process_environment(Args && args) + { + if (std::begin(args) != std::end(args)) + build_env(std::forward(args), *std::begin(args)); + } + + + std::vector ascii_env; + std::vector unicode_env; + + + error_code on_setup(windows::default_launcher & launcher, + const filesystem::path &, const std::wstring &); + +#else + + template + static + std::vector build_env(Args && args, + typename enable_if< + std::is_convertible< + decltype(*std::begin(std::declval())), + ASIO_CSTRING_VIEW>::value>::type * = nullptr) + { + std::vector env; + for (auto && e : args) + env.push_back(e.c_str()); + + env.push_back(nullptr); + return env; + } + + template + std::vector build_env(Args && args, + typename enable_if< + !std::is_convertible< + decltype(*std::begin(std::declval())), + ASIO_CSTRING_VIEW>::value>::type * = nullptr) + { + std::vector env; + + using char_type = typename decay()))[0])>::type; + for (ASIO_BASIC_STRING_VIEW_PARAM(char_type) arg : args) + env_buffer.push_back(detail::convert_chars(arg.data(), arg.data() + arg.size(), ' ')); + + for (auto && e : env_buffer) + env.push_back(e.c_str()); + env.push_back(nullptr); + return env; + } + + + process_environment(std::initializer_list sv) : env{build_env(sv)} { } + + template + process_environment(Args && args) : env(build_env(std::forward(args))) + { + } + + + error_code on_setup(posix::default_launcher & launcher, + const filesystem::path &, const char * const *); + + std::vector env; + std::vector env_buffer; + +#endif + +}; + + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif #endif //BOOST_PROCESS_V2_ENVIRONMENT_HPP diff --git a/include/boost/process/v2/exit_code.hpp b/include/boost/process/v2/exit_code.hpp new file mode 100644 index 000000000..38b8756ff --- /dev/null +++ b/include/boost/process/v2/exit_code.hpp @@ -0,0 +1,70 @@ +// +// process/exit_code.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_EXIT_CODE_HPP +#define BOOST_PROCESS_V2_EXIT_CODE_HPP + +#include + +#if defined(BOOST_PROCESS_V2_POSIX) +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +typedef unsigned long native_exit_code_type; + +namespace detail +{ +constexpr native_exit_code_type still_active = 259u; +} + +inline bool process_is_running(native_exit_code_type code) +{ + return code == detail::still_active; +} + +inline int evaluate_exit_code(native_exit_code_type code) +{ + return static_cast(code); +} + +#else + +typedef int native_exit_code_type; + +namespace detail +{ +constexpr native_exit_code_type still_active = 0x7f; +} + +inline bool process_is_running(int code) +{ + return !WIFEXITED(code) && !WIFSIGNALED(code); +} + +inline int evaluate_exit_code(int code) +{ + if (WIFEXITED(code)) + return WEXITSTATUS(code); + else if (WIFSIGNALED(code)) + return WTERMSIG(code); + else + return code; +} + +#endif + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_EXIT_CODE_HPP \ No newline at end of file diff --git a/include/boost/process/v2/impl/default_launcher.ipp b/include/boost/process/v2/impl/default_launcher.ipp new file mode 100644 index 000000000..1282ad523 --- /dev/null +++ b/include/boost/process/v2/impl/default_launcher.ipp @@ -0,0 +1,22 @@ +// +// boost/process/v2/default_launcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_IMPL_DEFAULT_LAUNCHER_IPP +#define BOOST_PROCESS_V2_IMPL_DEFAULT_LAUNCHER_IPP + +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#endif + + + +#endif //BOOST_PROCESS_V2_IMPL_DEFAULT_LAUNCHER_IPP \ No newline at end of file diff --git a/include/boost/process/v2/impl/environment.ipp b/include/boost/process/v2/impl/environment.ipp new file mode 100644 index 000000000..60de7636f --- /dev/null +++ b/include/boost/process/v2/impl/environment.ipp @@ -0,0 +1,49 @@ +// +// boost/process/v2/impl/environment.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_IMPL_ENVIRONMENT_IPP +#define BOOST_PROCESS_V2_IMPL_ENVIRONMENT_IPP + +#include +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(BOOST_PROCESS_V2_WINDOWS) + +error_code process_environment::on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &) +{ + if (!unicode_env.empty()) + { + launcher.creation_flags |= CREATE_UNICODE_ENVIRONMENT ; + launcher.environment = unicode_env.data(); + } + else if (!ascii_env.empty()) + launcher.environment = ascii_env.data(); + + return error_code {}; +}; + +#else + +error_code process_environment::on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *) +{ + launcher.env = env.data(); + return error_code{}; +}; + +#endif + + +BOOST_PROCESS_V2_END_NAMESPACE + + +#endif //BOOST_PROCESS_V2_IMPL_ENVIRONMENT_IPP \ No newline at end of file diff --git a/include/boost/process/v2/impl/process_handle.ipp b/include/boost/process/v2/impl/process_handle.ipp new file mode 100644 index 000000000..813395c21 --- /dev/null +++ b/include/boost/process/v2/impl/process_handle.ipp @@ -0,0 +1,17 @@ +// 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_IMPL_PROCESS_HANDLE_IPP +#define BOOST_PROCESS_V2_IMPL_PROCESS_HANDLE_IPP + +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else + + +#endif + +#endif //BOOST_PROCESS_V2_IMPL_PROCESS_HANDLE_IPP diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp new file mode 100644 index 000000000..69456989e --- /dev/null +++ b/include/boost/process/v2/process.hpp @@ -0,0 +1,335 @@ +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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) +// +// +// process.hpp +// ~~~~~~~~~~~~~~ +// + +#ifndef BOOST_PROCESS_V2_PROCESS_HPP +#define BOOST_PROCESS_V2_PROCESS_HPP + +#include +#include +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#else +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + + +template +struct basic_process +{ + using executor_type = Executor; + executor_type get_executor() {return process_handle_.get_executor();} + + /// Provides access to underlying operating system facilities + using native_handle_type = typename basic_process_handle::native_handle_type; + + /// Rebinds the process_handle to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_process other; + }; + + /** An empty process is similar to a default constructed thread. It holds an empty + handle and is a place holder for a process that is to be launched later. */ + basic_process() = default; + + basic_process(const basic_process&) = delete; + basic_process& operator=(const basic_process&) = delete; + + basic_process(basic_process&& lhs) + : attached_(lhs.attached_), + terminated_(lhs.terminated_), + exit_status_{lhs.exit_status_}, + process_handle_(std::move(lhs.process_handle_)) + + { + lhs.attached_ = false; + } + basic_process& operator=(basic_process&& lhs) + { + attached_ = lhs.attached_; + terminated_ = lhs.terminated_; + exit_status_ = lhs.exit_status_; + process_handle_ = std::move(lhs.process_handle_); + lhs.attached_ = false; + return *this; + } + + /// Construct a child from a property list and launch it. + template + explicit basic_process( + executor_type executor, + const filesystem::path& exe, + std::initializer_list args, + Inits&&... inits) + : basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward(inits)...)) + { + } + /// Construct a child from a property list and launch it. + template + explicit basic_process( + executor_type executor, + const filesystem::path& exe, + std::initializer_list args, + Inits&&... inits) + : basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward(inits)...)) + { + } + + /// Construct a child from a property list and launch it. + template + explicit basic_process( + executor_type executor, + const filesystem::path& exe, + Args&& args, Inits&&... inits) + : basic_process(default_process_launcher()(std::move(executor), exe, + std::forward(args), std::forward(inits)...)) + { + } + + /// Construct a child from a property list and launch it. + template + explicit basic_process( + ExecutionContext & context, + typename std::enable_if< + std::is_convertible::value, + const filesystem::path&>::type exe, + std::initializer_list args, + Inits&&... inits) + : basic_process(default_process_launcher()(executor_type(context.get_executor()), exe, args, std::forward(inits)...)) + { + } + + /// Construct a child from a property list and launch it. + template + explicit basic_process( + ExecutionContext & context, + typename std::enable_if< + std::is_convertible::value, + const filesystem::path&>::type exe, + std::initializer_list args, + Inits&&... inits) + : basic_process(default_process_launcher()(executor_type(context.get_executor()), exe, args, std::forward(inits)...)) + { + } + + /// Construct a child from a property list and launch it. + template + explicit basic_process( + ExecutionContext & context, + typename std::enable_if< + std::is_convertible::value, + const filesystem::path&>::type exe, + Args&& args, Inits&&... inits) + : basic_process(default_process_launcher()(executor_type(context.get_executor()), + exe, std::forward(args), std::forward(inits)...)) + { + } + + /// Attach to an existing process + explicit basic_process(executor_type exec, pid_type pid) : process_handle_{std::move(exec), pid} {} + + /// Attach to an existing process and the internal handle + explicit basic_process(executor_type exec, pid_type pid, native_handle_type native_handle) + : process_handle_{std::move(exec), pid, native_handle} {} + + /// Create an invalid handle + explicit basic_process(executor_type exec) : process_handle_{std::move(exec)} {} + + /// Attach to an existing process + template + explicit basic_process(ExecutionContext & context, pid_type pid, + typename std::enable_if< + std::is_convertible::value, void *>::type = nullptr) + : process_handle_{context, pid} {} + + /// Attach to an existing process and the internal handle + template + explicit basic_process(ExecutionContext & context, pid_type pid, native_handle_type native_handle, + typename std::enable_if< + std::is_convertible::value, void *>::type = nullptr) + : process_handle_{context, pid, native_handle} {} + + /// Create an invalid handle + template + explicit basic_process(ExecutionContext & context, + typename std::enable_if< + is_convertible::value, void *>::type = nullptr) + : process_handle_{context} {} + + + + // tbd behavior + ~basic_process() + { + if (attached_ && !terminated_) + process_handle_.terminate_if_running(); + } + + void interrupt() + { + error_code ec; + interrupt(ec); + if (ec) + throw system_error(ec, "interrupt failed"); + + } + void interrupt(error_code & ec) + { + process_handle_.interrupt(ec); + } + + void request_exit() + { + error_code ec; + request_exit(ec); + if (ec) + throw system_error(ec, "request_exit failed"); + } + void request_exit(error_code & ec) + { + process_handle_.request_exit(ec); + } + + void terminate() + { + error_code ec; + terminate(ec); + if (ec) + detail::throw_error(ec, "terminate failed"); + } + void terminate(error_code & ec) + { + process_handle_.terminate(exit_status_, ec); + } + + int wait() + { + error_code ec; + if (running(ec)) + wait(ec); + if (ec) + detail::throw_error(ec, "wait failed"); + return exit_code(); + } + int wait(error_code & ec) + { + if (running(ec)) + process_handle_.wait(exit_status_, ec); + return exit_code(); + } + + void detach() + { + attached_ = false; + } + void join() {wait();} + bool joinable() {return attached_ && process_handle_.is_open(); } + + native_handle_type native_handle() {return process_handle_.native_handle(); } + int exit_code() const + { + return evaluate_exit_code(exit_status_); + } + + pid_type id() const {return process_handle_.id();} + + native_exit_code_type native_exit_code() const + { + return exit_status_; + } + + bool running() + { + error_code ec; + native_exit_code_type exit_code; + auto r = process_handle_.running(exit_code, ec); + if (!ec) + exit_status_ = exit_code; + else + throw system_error(ec, "running failed"); + + return r; + } + bool running(error_code & ec) noexcept + { + native_exit_code_type exit_code ; + auto r = process_handle_.running(exit_code, ec); + if (!ec) + exit_status_ = exit_code; + return r; + } + + bool is_open() const { return process_handle_.is_open(); } + explicit operator bool() const {return valid(); } + + + template + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, int)) + async_wait(WaitHandler && handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( + async_wait_op_{process_handle_, exit_status_}, handler, process_handle_); + } + +private: + basic_process_handle process_handle_; + bool attached_{true}; + bool terminated_{false}; + native_exit_code_type exit_status_{detail::still_active}; + + + struct async_wait_op_ + { + basic_process_handle & handle; + native_exit_code_type & res; + + template + void operator()(Self && self) + { + handle.async_wait(std::move(self)); + } + + template + void operator()(Self && self, error_code ec, native_exit_code_type code) + { + if (!ec && process_is_running(code)) + handle.async_wait(std::move(self)); + else + { + if (!ec) + res = code; + std::move(self).complete(ec, evaluate_exit_code(code)); + } + } + }; +}; + + +typedef basic_process<> process; + +BOOST_PROCESS_V2_END_NAMESPACE + + +#endif //BOOST_PROCESS_V2_PROCESS_HPP \ No newline at end of file diff --git a/include/boost/process/v2/process_handle.hpp b/include/boost/process/v2/process_handle.hpp new file mode 100644 index 000000000..926fe23e2 --- /dev/null +++ b/include/boost/process/v2/process_handle.hpp @@ -0,0 +1,25 @@ +// 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_PROCESS_HANDLE_HPP +#define BOOST_PROCESS_V2_PROCESS_HANDLE_HPP + +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else + +#endif + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + + + + +#endif //BOOST_PROCESS_V2_PROCESS_HANDLE_HPP diff --git a/include/boost/process/v2/src.hpp b/include/boost/process/v2/src.hpp index 290fc445c..60c505ce6 100644 --- a/include/boost/process/v2/src.hpp +++ b/include/boost/process/v2/src.hpp @@ -19,4 +19,8 @@ #include #include #include +#include +#include +#include + #endif //BOOST_PROCESS_V2_SRC_HPP diff --git a/include/boost/process/v2/start_dir.hpp b/include/boost/process/v2/start_dir.hpp new file mode 100644 index 000000000..32bcb05d0 --- /dev/null +++ b/include/boost/process/v2/start_dir.hpp @@ -0,0 +1,48 @@ +// +// process/start_dir.hpp +// ~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_START_DIR_HPP +#define BOOST_PROCESS_v2_START_DIR_HPP + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +struct process_start_dir +{ + filesystem::path start_dir; + + process_start_dir(filesystem::path start_dir) : start_dir(std::move(start_dir)) + { + } + +#if defined(BOOST_PROCESS_V2_WINDOWS) + error_code on_setup(windows::default_launcher & launcher, + const filesystem::path &, const std::wstring &) + { + launcher.current_directory = start_dir; + return error_code {}; + }; + +#else + error_code on_exec_setup(posix::default_launcher & launcher, + const filesystem::path &, const char * const *) + { + if (::chdir(start_dir.c_str()) == -1) + return get_last_error(); + else + return error_code (); + } +#endif + +}; + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_v2_START_DIR_HPP \ No newline at end of file diff --git a/include/boost/process/v2/stdio.hpp b/include/boost/process/v2/stdio.hpp new file mode 100644 index 000000000..73c95824e --- /dev/null +++ b/include/boost/process/v2/stdio.hpp @@ -0,0 +1,187 @@ +// +// process/stdio.hpp +// ~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_STDIO_HPP +#define BOOST_PROCESS_V2_STDIO_HPP + +#include +#include + + +#if defined(BOOST_PROCESS_V2_POSIX) +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace detail +{ +#if defined(BOOST_PROCESS_V2_WINDOWS) + +extern "C" intptr_t _get_osfhandle(int fd); + +struct handle_closer +{ + handle_closer() = default; + handle_closer(bool close) : close(close) {} + handle_closer(DWORD flags) : close(false), flags{flags} {} + + + void operator()(HANDLE h) const + { + if (close) + ::CloseHandle(h); + else if (flags != 0xFFFFFFFFu) + ::SetHandleInformation(h, 0xFFFFFFFFu, flags); + + } + + bool close{false}; + DWORD flags{0xFFFFFFFFu}; +}; + +template +struct process_io_binding +{ + HANDLE prepare() + { + auto hh = h.get(); + ::SetHandleInformation(hh, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + return hh; + } + + std::unique_ptr h{::GetStdHandle(Io), false}; + + static DWORD get_flags(HANDLE h) + { + DWORD res; + if (!::GetHandleInformation(h, &res)) + detail::throw_last_error("get_flags"); + return res; + } + + process_io_binding() = default; + + template + process_io_binding(Stream && str, decltype(std::declval().native_handle()) = nullptr) + : process_io_binding(str.native_handle()) + {} + + process_io_binding(FILE * f) : process_io_binding(_get_osfhandle(_fileno(f))) {} + process_io_binding(HANDLE h) : h{h, get_flags(h)} {} + process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("NUL")) {} + process_io_binding(const filesystem::path & pth) + : h(::CreateFileW( + pth.c_str(), + Io == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr + ), true) + { + } + + +}; + +typedef process_io_binding process_input_binding; +typedef process_io_binding process_output_binding; +typedef process_io_binding process_error_binding; + +#else + +template +struct process_io_binding +{ + constexpr static int target = Target; + int fd{target}; + bool fd_needs_closing{false}; + + ~process_io_binding() + { + if (fd_needs_closing) + ::close(fd); + } + + process_io_binding() = default; + + template + process_io_binding(Stream && str, decltype(std::declval().native_handle()) = -1) + : process_io_binding(str.native_handle()) + {} + + process_io_binding(FILE * f) : process_io_binding(fileno(f)) {} + process_io_binding(int fd) : fd(fd) {} + process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("/dev/null")) {} + process_io_binding(const filesystem::path & pth) + : fd(::open(pth.c_str(), + Target == STDIN_FILENO ? O_RDONLY : (O_WRONLY | O_CREAT), + 0660)), fd_needs_closing(true) + { + } + + error_code on_exec_setup(posix::default_launcher & launcher, + const filesystem::path &, const char * const *) + { + if (::dup2(fd, target) == -1) + return error_code(errno, system_category()); + else + return error_code (); + } +}; + +typedef process_io_binding process_input_binding; +typedef process_io_binding process_output_binding; +typedef process_io_binding process_error_binding; + +#endif + +} + +struct process_stdio +{ + detail::process_input_binding in; + detail::process_output_binding out; + detail::process_error_binding err; + +#if defined(BOOST_PROCESS_V2_WINDOWS) + error_code on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &) + { + + launcher.startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; + launcher.startup_info.StartupInfo.hStdInput = in.prepare(); + launcher.startup_info.StartupInfo.hStdOutput = out.prepare(); + launcher.startup_info.StartupInfo.hStdError = err.prepare(); + launcher.inherit_handles = true; + return error_code {}; + }; +#else + error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *) + { + if (::dup2(in.fd, in.target) == -1) + return error_code(errno, system_category()); + + if (::dup2(out.fd, out.target) == -1) + return error_code(errno, system_category()); + + if (::dup2(err.fd, err.target) == -1) + return error_code(errno, system_category()); + + return error_code {}; + }; +#endif + +}; + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_STDIO_HPP \ No newline at end of file diff --git a/include/boost/process/v2/windows/as_user_launcher.hpp b/include/boost/process/v2/windows/as_user_launcher.hpp new file mode 100644 index 000000000..7157d0b08 --- /dev/null +++ b/include/boost/process/v2/windows/as_user_launcher.hpp @@ -0,0 +1,137 @@ +// +// boost/process/v2/windows/default_launcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_WINDOWS_AS_USER_LAUNCHER_HPP +#define BOOST_PROCESS_V2_WINDOWS_AS_USER_LAUNCHER_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace windows +{ + +/// The default launcher for processes on windows. +struct as_user_launcher : default_launcher +{ + HANDLE token; + as_user_launcher(HANDLE token = INVALID_HANDLE_VALUE) : token(token) {} + + + template + auto operator()(ExecutionContext & context, + error_code & ec, + const typename std::enable_if::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + error_code ec; + auto proc = (*this)(context, ec, path, std::forward(args), std::forward(inits)...); + + if (ec) + asio::detail::throw_error(ec, "as_user_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(), path, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + error_code ec; + auto proc = (*this)(std::move(exec), ec, path, std::forward(args), std::forward(inits)...); + + if (ec) + asio::detail::throw_error(ec, "as_user_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto command_line = this->build_command_line_(executable, args); + + ec = on_init_(*this, executable, command_line, inits...); + if (ec) + { + detail::on_error(*this, executable, command_line, ec, inits...); + return basic_process(exec); + } + auto ok = ::CreateProcessAsUserW( + token, + executable.empty() ? nullptr : executable.c_str(), + command_line.empty() ? nullptr : command_line.c_str(), + process_attributes, + thread_attributes, + inherit_handles ? TRUE : FALSE, + creation_flags, + environment, + current_directory.empty() ? nullptr : current_directory.c_str(), + &startup_info, + &process_information); + + + if (ok == 0) + { + ec.assign(::GetLastError(), error::get_system_category()); + detail::on_error(*this, executable, command_line, ec, inits...); + + if (process_information.hProcess != INVALID_HANDLE) + ::CloseHandle(process_information.hProcess); + if (process_information.hThread != INVALID_HANDLE) + ::CloseHandle(process_information.hThread); + + return basic_process(exec); + } else + { + detail::on_success(*this, executable, command_line, inits...); + + if (process_information.hThread != INVALID_HANDLE) + ::CloseHandle(process_information.hThread); + + return basic_process(exec, + this->process_information.dwProcessId, + this->process_information.hProcess); + } + } +}; + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_WINDOWS_AS_USER_LAUNCHER_HPP \ No newline at end of file diff --git a/include/boost/process/v2/windows/creation_flags.hpp b/include/boost/process/v2/windows/creation_flags.hpp new file mode 100644 index 000000000..1987dd8db --- /dev/null +++ b/include/boost/process/v2/windows/creation_flags.hpp @@ -0,0 +1,39 @@ +// +// boost/process/v2/windows/default_launcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_WINDOWS_CREATION_FLAGS_HPP +#define BOOST_PROCESS_V2_WINDOWS_CREATION_FLAGS_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace windows +{ + +template +struct process_creation_flags +{ + constexpr process_creation_flags () {} + + error_code on_setup(windows::default_launcher & launcher, + const filesystem::path &, + const std::wstring &) const + { + launcher.startup_info.StartupInfo.dwFlags |= Flags; + return error_code {}; + }; +}; + +constexpr static process_creation_flags create_new_process_group; + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_WINDOWS_CREATION_FLAGS_HPP \ No newline at end of file diff --git a/include/boost/process/v2/windows/default_launcher.hpp b/include/boost/process/v2/windows/default_launcher.hpp new file mode 100644 index 000000000..9c59d44f6 --- /dev/null +++ b/include/boost/process/v2/windows/default_launcher.hpp @@ -0,0 +1,380 @@ +// +// boost/process/v2/windows/default_launcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_WINDOWS_DEFAULT_LAUNCHER_HPP +#define BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP + +#include +#include +#include +#include + +#include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#include +#include +#else +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +template +struct basic_process; + +namespace detail +{ + +struct base {}; +struct derived : base {}; + +template +inline error_code invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + Init && init, base && ) +{ + return error_code{}; +} + +template +inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + Init && init, derived && ) +-> decltype(init.on_setup(launcher, executable, cmd_line)) +{ + return init.on_setup(launcher, executable, cmd_line); +} + +template +inline std::false_type probe_on_setup( + Launcher & launcher, Init && init, base && ); + +template +inline auto probe_on_setup(Launcher & launcher, Init && init, derived && ) + -> std::is_same(), std::declval()))>; + +template +using has_on_setup = decltype(probe_on_setup(std::declval(), std::declval(), derived{})); + +template +inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line) +{ + return error_code{}; +} + +template +inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + Init1 && init1, Inits && ... inits) +{ + auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{}); + if (ec) + return ec; + else + return on_setup(launcher, executable, cmd_line, inits...); +} + + +template +inline void invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + const error_code & ec, Init && init, base && ) +{ +} + +template +inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + const error_code & ec, Init && init, derived && ) +-> decltype(init.on_error(launcher, ec, executable, cmd_line, ec)) +{ + init.on_error(launcher, executable, cmd_line, ec); +} + + +template +inline std::false_type probe_on_error( + Launcher & launcher, Init && init, base && ); + +template +inline auto probe_on_error(Launcher & launcher, Init && init, derived && ) + -> std::is_same(), std::declval(), std::declval()))>; + +template +using has_on_error = decltype(probe_on_error(std::declval(), std::declval(), derived{})); + + +template +inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + const error_code & ec) +{ +} + +template +inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + const error_code & ec, + Init1 && init1, + Inits && ... inits) +{ + invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{}); + on_error(launcher, executable, cmd_line, ec, inits...); +} + +template +inline void invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + Init && init, base && ) +{ +} + +template +inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + Init && init, derived && ) + -> decltype(init.on_success(launcher, executable, cmd_line)) +{ + init.on_success(launcher, executable, cmd_line); +} + +template +inline std::false_type probe_on_success( + Launcher & launcher, Init && init, base && ); + +template +inline auto probe_on_success(Launcher & launcher, Init && init, derived && ) + -> std::is_same(), std::declval()))>; + +template +using has_on_success = decltype(probe_on_success(std::declval(), std::declval(), derived{})); + +template +inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line) +{ +} + +template +inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + Init1 && init1, Inits && ... inits) +{ + invoke_on_success(launcher, executable, cmd_line, init1, derived{}); + on_success(launcher, executable, cmd_line, inits...); +} + +template +struct is_initializer : std::integral_constant::value || + has_on_error::value || + has_on_success::value> +{ +}; + +} + +template +struct basic_process; + +namespace windows +{ + +/// The default launcher for processes on windows. +struct default_launcher +{ + SECURITY_ATTRIBUTES * process_attributes = nullptr; + SECURITY_ATTRIBUTES * thread_attributes = nullptr; + bool inherit_handles = false; + DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT}; + void * environment = nullptr; + filesystem::path current_directory{}; + + STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr, + ::GetStdHandle(STD_INPUT_HANDLE), + ::GetStdHandle(STD_OUTPUT_HANDLE), + ::GetStdHandle(STD_ERROR_HANDLE )}, + nullptr}; + PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0}; + + default_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) + asio::detail::throw_error(ec, "default_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(), executable, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value + || BOOST_PROCESS_V2_ASIO_NAMESPACE::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) + asio::detail::throw_error(ec, "default_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto command_line = this->build_command_line(executable, std::forward(args)); + + ec = detail::on_setup(*this, executable, command_line, inits...); + if (ec) + { + detail::on_error(*this, executable, command_line, ec, inits...); + return basic_process(exec); + } + + auto ok = ::CreateProcessW( + executable.empty() ? nullptr : executable.c_str(), + command_line.empty() ? nullptr : &command_line.front(), + process_attributes, + thread_attributes, + inherit_handles ? TRUE : FALSE, + creation_flags, + environment, + current_directory.empty() ? nullptr : current_directory.c_str(), + &startup_info.StartupInfo, + &process_information); + + auto ec__ = detail::get_last_error(); + if (ok == 0) + { + ec = detail::get_last_error(); + detail::on_error(*this, executable, command_line, ec, inits...); + + if (process_information.hProcess != INVALID_HANDLE_VALUE) + ::CloseHandle(process_information.hProcess); + if (process_information.hThread != INVALID_HANDLE_VALUE) + ::CloseHandle(process_information.hThread); + + return basic_process(exec); + } + else + { + detail::on_success(*this, executable, command_line, inits...); +/* + if (process_information.hThread != INVALID_HANDLE_VALUE) + ::CloseHandle(process_information.hThread); +*/ + return basic_process(exec, + this->process_information.dwProcessId, + this->process_information.hProcess); + } + } + + BOOST_PROCESS_V2_DECL static + std::size_t escaped_argv_length(basic_string_view ws); + BOOST_PROCESS_V2_DECL static + std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size, + basic_string_view ws); + + + + + template + static std::wstring build_command_line_impl( + const filesystem::path & pt, + const Argv & argv, + basic_string_view args) + { + std::size_t req_size = std::accumulate( + std::begin(argv), std::end(argv), escaped_argv_length(pt.native()), + [](std::size_t sz, basic_string_view arg) -> std::size_t + { + return sz + 1u + escaped_argv_length(arg); + }); + + std::wstring res; + res.resize(req_size, L' '); + + wchar_t * itr = &res.front(); + itr += escape_argv_string(itr, res.size(), pt.native()); + for (const auto & a : argv) + { + itr++; + itr += escape_argv_string(itr, std::distance(itr, &res.back() + 1), a); + } + return res; + } + + template + static std::wstring build_command_line_impl( + const filesystem::path & pt, + const Argv & argv, + basic_string_view args) + { + std::vector argw; + argw.resize(std::distance(std::begin(argv), std::end(argv))); + std::transform(std::begin(argv), std::end(argv), argw.begin(), + [](basic_string_view arg) + { + return detail::conv_string(arg.data(), arg.size()); + }); + return build_command_line_impl(pt, argw, L""); + } + + template()))> + static std::wstring build_command_line(const filesystem::path & pt, const Args & args) + { + if (std::begin(args) == std::end(args)) + return pt.native(); + + return build_command_line_impl(pt, args, *std::begin(args)); + } + +}; + + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) +#include +#endif + +#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP \ No newline at end of file diff --git a/include/boost/process/v2/windows/impl/default_launcher.ipp b/include/boost/process/v2/windows/impl/default_launcher.ipp new file mode 100644 index 000000000..fa6947562 --- /dev/null +++ b/include/boost/process/v2/windows/impl/default_launcher.ipp @@ -0,0 +1,80 @@ +// +// boost/process/v2/windows/impl/default_launcher.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_WINDOWS_IMPL_DEFAULT_LAUNCHER_IPP +#define BOOST_PROCESS_V2_WINDOWS_IMPL_DEFAULT_LAUNCHER_IPP + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace windows +{ + + std::size_t default_launcher::escaped_argv_length(basic_string_view ws) + { + if (ws.empty()) + return 2u; // just quotes + + constexpr static auto space = L' '; + constexpr static auto quote = L'"'; + + const auto has_space = ws.find(space) != basic_string_view::npos; + const auto quoted = (ws.front() == quote) && (ws.back() == quote); + const auto needs_escape = has_space && !quoted ; + + if (!needs_escape) + return ws.size(); + else + return ws.size() + std::count(ws.begin(), ws.end(), quote) + 2u; + } + + + std::size_t default_launcher::escape_argv_string(wchar_t * itr, std::size_t max_size, + basic_string_view ws) + { + const auto sz = escaped_argv_length(ws); + if (sz > max_size) + return 0u; + if (ws.empty()) + { + itr[0] = L'"'; + itr[1] = L'"'; + return 2u; + } + + const auto has_space = ws.find(L' ') != basic_string_view::npos; + const auto quoted = (ws.front() == L'"') && (ws.back() == L'"'); + const auto needs_escape = has_space && !quoted; + + if (!needs_escape) + return std::copy(ws.begin(), ws.end(), itr) - itr; + + if (sz < (2u + ws.size())) + return 0u; + + const auto end = itr + sz; + const auto begin = itr; + *(itr ++) = L'"'; + for (auto wc : ws) + { + if (wc == L'"') + *(itr++) = L'\\'; + *(itr++) = wc; + } + + *(itr ++) = L'"'; + return itr - begin; + } + +} +BOOST_PROCESS_V2_END_NAMESPACE + + +#endif //BOOST_PROCESS_V2_WINDOWS_IMPL_DEFAULT_LAUNCHER_IPP \ No newline at end of file diff --git a/include/boost/process/v2/windows/show_window.hpp b/include/boost/process/v2/windows/show_window.hpp new file mode 100644 index 000000000..31d61ebcf --- /dev/null +++ b/include/boost/process/v2/windows/show_window.hpp @@ -0,0 +1,52 @@ +// +// boost/process/v2/windows/default_launcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_WINDOWS_SHOW_WINDOW_HPP +#define BOOST_PROCESS_V2_WINDOWS_SHOW_WINDOW_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace windows +{ + +template +struct process_show_window +{ + constexpr process_show_window() {} + + error_code on_setup(windows::default_launcher & launcher, + const filesystem::path &, + const std::wstring &) const + { + launcher.startup_info.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW; + launcher.startup_info.StartupInfo.wShowWindow |= Flags; + + return error_code {}; + }; +}; + +///Hides the window and activates another window. +constexpr static process_show_window show_window_hide; +///Activates the window and displays it as a maximized window. +constexpr static process_show_window show_window_maximized; +///Activates the window and displays it as a minimized window. +constexpr static process_show_window show_window_minimized; +///Displays the window as a minimized window. This value is similar to `minimized`, except the window is not activated. +constexpr static process_show_window show_window_minimized_not_active; +///Displays a window in its most recent size and position. This value is similar to show_normal`, except that the window is not activated. +constexpr static process_show_window show_window_not_active; +///Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when displaying the window for the first time. +constexpr static process_show_window show_window_normal; + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_WINDOWS_SHOW_WINDOW_HPP \ No newline at end of file diff --git a/include/boost/process/v2/windows/with_logon_launcher.hpp b/include/boost/process/v2/windows/with_logon_launcher.hpp new file mode 100644 index 000000000..6c2dafeb7 --- /dev/null +++ b/include/boost/process/v2/windows/with_logon_launcher.hpp @@ -0,0 +1,151 @@ +// +// boost/process/v2/windows/default_launcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_WINDOWS_WITH_LOGON_LAUNCHER_HPP +#define BOOST_PROCESS_V2_WINDOWS_WITH_LOGON_LAUNCHER_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace windows +{ + +/// The default launcher for processes on windows. +struct with_logon_launcher : default_launcher +{ + std::wstring username, domain, password; + DWORD logon_flags{0u}; + + with_logon_launcher(std::wstring username = L"", + std::wstring password = L"", + std::wstring domain = L"", + DWORD logon_flags = 0u) : + username(std::move(username)), + password(std::move(password)), + domain(std::move(domain)), + logon_flags(logon_flags) + { + } + + + template + auto operator()(ExecutionContext & context, + error_code & ec, + const typename std::enable_if::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + error_code ec; + auto proc = (*this)(context, ec, path, std::forward(args), std::forward(inits)...); + + if (ec) + asio::detail::throw_error(ec, "with_logon_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(), path, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + error_code ec; + auto proc = (*this)(std::move(exec), ec, path, std::forward(args), std::forward(inits)...); + + if (ec) + asio::detail::throw_error(ec, "with_logon_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto command_line = this->build_command_line_(executable, args); + + ec = on_init_(*this, executable, command_line, inits...); + if (ec) + { + detail::on_error(*this, executable, command_line, ec, inits...); + return basic_process(exec); + } + auto ok = ::CreateProcessWithLogonW( + username.c_str(), + domain.empty() ? nullptr : domain.c_str(), + password.c_str(), + logon_flags + executable.empty() ? nullptr : executable.c_str(), + command_line.empty() ? nullptr : command_line.c_str(), + process_attributes, + thread_attributes, + inherit_handles ? TRUE : FALSE, + creation_flags, + environment, + current_directory.empty() ? nullptr : current_directory.c_str(), + &startup_info, + &process_information); + + + if (ok == 0) + { + ec.assign(::GetLastError(), error::get_system_category()); + detail::on_error(*this, executable, command_line, ec, inits...); + + if (process_information.hProcess != INVALID_HANDLE) + ::CloseHandle(process_information.hProcess); + if (process_information.hThread != INVALID_HANDLE) + ::CloseHandle(process_information.hThread); + + return basic_process(exec); + } else + { + detail::on_success(*this, executable, command_line, inits...); + + if (process_information.hThread != INVALID_HANDLE) + ::CloseHandle(process_information.hThread); + + return basic_process(exec, + this->process_information.dwProcessId, + this->process_information.hProcess); + } + } +}; + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_WINDOWS_WITH_LOGON_LAUNCHER_HPP \ No newline at end of file diff --git a/include/boost/process/v2/windows/with_token_launcher.hpp b/include/boost/process/v2/windows/with_token_launcher.hpp new file mode 100644 index 000000000..a03bdc349 --- /dev/null +++ b/include/boost/process/v2/windows/with_token_launcher.hpp @@ -0,0 +1,140 @@ +// +// boost/process/v2/windows/default_launcher.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_WINDOWS_WITH_TOKEN_LAUNCHER_HPP +#define BOOST_PROCESS_V2_WINDOWS_WITH_TOKEN_LAUNCHER_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace windows +{ + +/// The default launcher for processes on windows. +struct with_token_launcher : default_launcher +{ + HANDLE token; + DWORD logon_flags; + with_token_launcher(HANDLE token = INVALID_HANDLE_VALUE, + DWORD logon_flags = 0u) : token(token), logon_flags(logon_flags) {} + + + template + auto operator()(ExecutionContext & context, + error_code & ec, + const typename std::enable_if::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + error_code ec; + auto proc = (*this)(context, ec, path, std::forward(args), std::forward(inits)...); + + if (ec) + asio::detail::throw_error(ec, "with_token_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(), path, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + error_code ec; + auto proc = (*this)(std::move(exec), ec, path, std::forward(args), std::forward(inits)...); + + if (ec) + asio::detail::throw_error(ec, "with_token_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto command_line = this->build_command_line_(executable, args); + + ec = on_init_(*this, executable, command_line, inits...); + if (ec) + { + detail::on_error(*this, executable, command_line, ec, inits...); + return basic_process(exec); + } + auto ok = ::CreateProcessWithTokenW ( + token, + logon_flags + executable.empty() ? nullptr : executable.c_str(), + command_line.empty() ? nullptr : command_line.c_str(), + process_attributes, + thread_attributes, + inherit_handles ? TRUE : FALSE, + creation_flags, + environment, + current_directory.empty() ? nullptr : current_directory.c_str(), + &startup_info, + &process_information); + + + if (ok == 0) + { + ec.assign(::GetLastError(), error::get_system_category()); + detail::on_error(*this, executable, command_line, ec, inits...); + + if (process_information.hProcess != INVALID_HANDLE) + ::CloseHandle(process_information.hProcess); + if (process_information.hThread != INVALID_HANDLE) + ::CloseHandle(process_information.hThread); + + return basic_process(exec); + } else + { + detail::on_success(*this, executable, command_line, inits...); + + if (process_information.hThread != INVALID_HANDLE) + ::CloseHandle(process_information.hThread); + + return basic_process(exec, + this->process_information.dwProcessId, + this->process_information.hProcess); + } + } +}; + +} +BOOST_PROCESS_V2_END_NAMESPACE + +#endif // BOOST_PROCESS_V2_WINDOWS_WITH_TOKEN_LAUNCHER_HPP \ No newline at end of file diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index bf190974f..0522b1114 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -15,5 +15,6 @@ boost_process_v2_standalone_test(utf8) boost_process_v2_standalone_test(cstring_ref) boost_process_v2_standalone_test(pid) boost_process_v2_standalone_test(environment) +boost_process_v2_standalone_test(exit_code) add_library(boost_process_v2_header_test header_1.cpp header_2.cpp) \ No newline at end of file diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index bfdb03e93..ee61e5dbf 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -12,6 +12,7 @@ if [ os.name ] = NT lib shell32 ; lib Advapi32 ; lib Ntdll ; + lib user32 ; } project : requirements @@ -28,6 +29,10 @@ project : requirements import testing ; +exe target : target.cpp : + off windows:shell32 windows:Ntdll + ; + alias filesystem : /boost//filesystem ; lib header_test : header_1.cpp header_2.cpp : @@ -37,6 +42,7 @@ lib test_impl : test_impl.cpp filesystem : BOOST_PROCESS_V2_SEPARATE_COMPILATION=1 static windows:shell32 + windows:user32 ; test-suite standalone : @@ -46,4 +52,7 @@ test-suite standalone : [ run pid.cpp test_impl ] ; +test-suite with_target : + [ run process.cpp test_impl : : target ] + ; diff --git a/test/v2/cstring_ref.cpp b/test/v2/cstring_ref.cpp index edac2e943..3f17841c6 100644 --- a/test/v2/cstring_ref.cpp +++ b/test/v2/cstring_ref.cpp @@ -96,4 +96,4 @@ BOOST_AUTO_TEST_CASE(cstring_view_test) BOOST_CHECK_LT(av.compare(bv), 0); BOOST_CHECK_GT(bv.compare(av), 0); -} \ No newline at end of file +} diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index ad0a3a797..8ce134fd1 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -133,4 +133,3 @@ BOOST_AUTO_TEST_CASE(wenvironment) BOOST_CHECK(bpe::key_value_pair(L"FOO", {L"X", L"YY", L"Z42"}) == cmp); #endif } - diff --git a/test/v2/pid.cpp b/test/v2/pid.cpp index 13b72372a..a0664bcc1 100644 --- a/test/v2/pid.cpp +++ b/test/v2/pid.cpp @@ -12,4 +12,4 @@ BOOST_AUTO_TEST_CASE(test_pid) { namespace bp2 = boost::process::v2; BOOST_CHECK_NE(bp2::current_pid(), static_cast(0)); -} +} \ No newline at end of file diff --git a/test/v2/process.cpp b/test/v2/process.cpp new file mode 100644 index 000000000..de11396f7 --- /dev/null +++ b/test/v2/process.cpp @@ -0,0 +1,63 @@ +// 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) + + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include + +#include +#include +#include + + + +namespace bpv = boost::process::v2; + +BOOST_AUTO_TEST_CASE(exit_code_sync) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + bpv::environment::set("BOOST_PROCESS_V2_TEST_SUBPROCESS", "test"); + boost::asio::io_context ctx; + + BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "0"}).wait(), 0); + BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "1"}).wait(), 1); + BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "2"}).wait(), 2); + BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "42"}).wait(), 42); + +} + +BOOST_AUTO_TEST_CASE(exit_code_async) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + bpv::environment::set("BOOST_PROCESS_V2_TEST_SUBPROCESS", "test"); + boost::asio::io_context ctx; + + int called = 0; + + bpv::process proc1(ctx, pth, {"exit-code", "0"}); + bpv::process proc2(ctx, pth, {"exit-code", "1"}); + bpv::process proc3(ctx, pth, {"exit-code", "2"}); + bpv::process proc4(ctx, pth, {"exit-code", "42"}); + + proc1.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); + proc2.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 1);}); + proc3.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 2);}); + proc4.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 42);}); + ctx.run(); + BOOST_CHECK_EQUAL(called, 4); +} + + + + diff --git a/test/v2/target.cpp b/test/v2/target.cpp new file mode 100644 index 000000000..bde8fbfc8 --- /dev/null +++ b/test/v2/target.cpp @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char * argv[]) +{ + std::string mode = argv[1]; + if (mode == "exit-code") + return std::stoi(argv[2]); + + return 0; +} \ No newline at end of file diff --git a/test/v2/test_impl.cpp b/test/v2/test_impl.cpp index 2abc39a59..bf27749fa 100644 --- a/test/v2/test_impl.cpp +++ b/test/v2/test_impl.cpp @@ -4,8 +4,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#define BOOST_TEST_MODULE process_test +#define BOOST_TEST_MODULE process_v2_test #include #include - diff --git a/test/v2/utf8.cpp b/test/v2/utf8.cpp index 8922ba13b..d4d731cc3 100755 --- a/test/v2/utf8.cpp +++ b/test/v2/utf8.cpp @@ -26,4 +26,4 @@ BOOST_AUTO_TEST_CASE(test_codecvt) BOOST_CHECK(boost::process::v2::detail::conv_string( in, end( in )) == win_t); BOOST_CHECK(boost::process::v2::detail::conv_string (win_t, end(win_t)) == in ); -} +} \ No newline at end of file From c6a812e401f53b09699e3f5fb001011f8e0c74fe Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sun, 29 May 2022 14:51:44 +0800 Subject: [PATCH 057/397] Added test & fixed some found bugs. --- include/boost/process/v2.hpp | 1 + include/boost/process/v2/environment.hpp | 55 ++- include/boost/process/v2/impl/environment.ipp | 6 +- include/boost/process/v2/process.hpp | 15 - .../process/v2/windows/default_launcher.hpp | 38 +- test/v2/Jamfile.jam | 5 +- test/v2/process.cpp | 333 +++++++++++++++++- test/v2/target.cpp | 41 ++- 8 files changed, 434 insertions(+), 60 deletions(-) diff --git a/include/boost/process/v2.hpp b/include/boost/process/v2.hpp index ad5f2d059..d18776e82 100644 --- a/include/boost/process/v2.hpp +++ b/include/boost/process/v2.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #endif //BOOST_PROCESS_V2_HPP diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index 718233dc7..f809c106f 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -1273,7 +1273,7 @@ inline boost::process::v2::filesystem::path find_executable( return false; }); if (itr != nullptr) - return itr->value_view(); + return itr->value(); else return value_view(); }; @@ -1285,13 +1285,13 @@ inline boost::process::v2::filesystem::path find_executable( for (auto ext : pathext) { boost::process::v2::filesystem::path nm(name); - nm += ext; + nm.concat(ext.begin(), ext.end()); - auto p = boost::process::v2::filesystem::path(pp_view) / nm; + auto p = boost::process::v2::filesystem::path(pp_view.begin(), pp_view.end()) / nm; error_code ec; - bool file = boost::process::v2::filesystem::is_regular_file(p, ec); - if (!ec && file && SHGetFileInfoW(p.native().c_str(), 0,0,0, SHGFI_EXETYPE)) + bool is_exec = detail::is_executable(p, ec); + if (!ec && is_exec) return p; } #else @@ -1300,8 +1300,8 @@ inline boost::process::v2::filesystem::path find_executable( { auto p = boost::process::v2::filesystem::path(pp_view) / name; error_code ec; - bool file = boost::process::v2::filesystem::is_regular_file(p, ec); - if (!ec && file && ::access(p.c_str(), X_OK) == 0) + bool is_exec = detail::is_executable(p, ec); + if (!ec && is_exec) return p; } #endif @@ -1491,43 +1491,42 @@ struct process_environment template void build_env(Args && args, string_view rs) { - std::vector vec; - // vec.reserve(std::end(args) - std::begin(args)); std::size_t length = 0u; - for (decltype(rs) v : std::forward(args)) - { - vec.push_back(v); - length += v.size() + 1u; - } + for (string_view v : args) + length += detail::size_as_wide(v.data(), v.size(), ec) + 1u; + + if (ec) + return; length ++ ; - ascii_env.resize(length); + unicode_env.resize(length); - auto itr = ascii_env.begin(); - for (const auto & v : vec ) + auto itr = &unicode_env.front(); + for (string_view v : args) { - itr = std::copy(v.begin(), v.end(), itr); + itr += detail::convert_to_wide( + v.data(), v.size(), + itr, &unicode_env.back() - itr, + ec); + if (ec) + break; *(itr++) = '\0'; } - ascii_env.back() = '\0'; + unicode_env.back() = '\0'; } template void build_env(Args && args, wstring_view rs) { - std::vector vec; -// vec.reserve(std::end(args) - std::begin(args)); std::size_t length = 0u; - for (decltype(rs) v : std::forward(args)) - { - vec.push_back(v); + for (const auto & v : std::forward(args)) length += v.size() + 1u; - } + length ++ ; unicode_env.resize(length); auto itr = unicode_env.begin(); - for (const auto & v : vec ) + for (wstring_view v : args ) { itr = std::copy(v.begin(), v.end(), itr); *(itr++) = L'\0'; @@ -1546,8 +1545,8 @@ struct process_environment build_env(std::forward(args), *std::begin(args)); } - - std::vector ascii_env; + error_code error() {return ec;} + error_code ec; std::vector unicode_env; diff --git a/include/boost/process/v2/impl/environment.ipp b/include/boost/process/v2/impl/environment.ipp index 60de7636f..d756bf1ab 100644 --- a/include/boost/process/v2/impl/environment.ipp +++ b/include/boost/process/v2/impl/environment.ipp @@ -21,15 +21,13 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE error_code process_environment::on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &) { - if (!unicode_env.empty()) + if (!unicode_env.empty() && !ec) { launcher.creation_flags |= CREATE_UNICODE_ENVIRONMENT ; launcher.environment = unicode_env.data(); } - else if (!ascii_env.empty()) - launcher.environment = ascii_env.data(); - return error_code {}; + return ec; }; #else diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index 69456989e..d36cb7b41 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -114,21 +114,6 @@ struct basic_process : basic_process(default_process_launcher()(executor_type(context.get_executor()), exe, args, std::forward(inits)...)) { } - - /// Construct a child from a property list and launch it. - template - explicit basic_process( - ExecutionContext & context, - typename std::enable_if< - std::is_convertible::value, - const filesystem::path&>::type exe, - std::initializer_list args, - Inits&&... inits) - : basic_process(default_process_launcher()(executor_type(context.get_executor()), exe, args, std::forward(inits)...)) - { - } - /// Construct a child from a property list and launch it. template explicit basic_process( diff --git a/include/boost/process/v2/windows/default_launcher.hpp b/include/boost/process/v2/windows/default_launcher.hpp index 9c59d44f6..2e2f22593 100644 --- a/include/boost/process/v2/windows/default_launcher.hpp +++ b/include/boost/process/v2/windows/default_launcher.hpp @@ -173,6 +173,23 @@ struct is_initializer : std::integral_constant +struct all_are_initializers; + +template +struct all_are_initializers : std::true_type {}; + + +template +struct all_are_initializers : is_initializer {}; + +template +struct all_are_initializers + : std::integral_constant::value && all_are_initializers::value> +{ +}; + + } template @@ -187,18 +204,23 @@ struct default_launcher SECURITY_ATTRIBUTES * process_attributes = nullptr; SECURITY_ATTRIBUTES * thread_attributes = nullptr; bool inherit_handles = false; - DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT}; + DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT }; void * environment = nullptr; filesystem::path current_directory{}; STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr, - ::GetStdHandle(STD_INPUT_HANDLE), - ::GetStdHandle(STD_OUTPUT_HANDLE), - ::GetStdHandle(STD_ERROR_HANDLE )}, + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE}, nullptr}; PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0}; + template + using enable_init = typename std::enable_if< + detail::all_are_initializers::value, + basic_process>::type; + default_launcher() = default; template @@ -207,7 +229,7 @@ struct default_launcher ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, filesystem::path >::type & executable, Args && args, - Inits && ... inits ) -> basic_process + Inits && ... inits ) -> enable_init { error_code ec; auto proc = (*this)(context, ec, executable, std::forward(args), std::forward(inits)...); @@ -226,7 +248,7 @@ struct default_launcher ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value, filesystem::path >::type & executable, Args && args, - Inits && ... inits ) -> basic_process + Inits && ... inits ) -> enable_init { return (*this)(context.get_executor(), executable, std::forward(args), std::forward(inits)...); } @@ -238,7 +260,7 @@ struct default_launcher || BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, filesystem::path >::type & executable, Args && args, - Inits && ... inits ) -> basic_process + Inits && ... inits ) -> enable_init { error_code ec; auto proc = (*this)(std::move(exec), ec, executable, std::forward(args), std::forward(inits)...); @@ -257,7 +279,7 @@ struct default_launcher BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, filesystem::path >::type & executable, Args && args, - Inits && ... inits ) -> basic_process + Inits && ... inits ) -> enable_init { auto command_line = this->build_command_line(executable, std::forward(args)); diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index ee61e5dbf..b0bffda4a 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -29,11 +29,12 @@ project : requirements import testing ; -exe target : target.cpp : +alias filesystem : /boost//filesystem ; + +exe target : target.cpp filesystem : off windows:shell32 windows:Ntdll ; -alias filesystem : /boost//filesystem ; lib header_test : header_1.cpp header_2.cpp : BOOST_PROCESS_V2_HEADER_ONLY=1 ; diff --git a/test/v2/process.cpp b/test/v2/process.cpp index de11396f7..c6ab59ec8 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -9,16 +9,60 @@ #define BOOST_ALL_NO_LIB 1 #endif // !defined(BOOST_ALL_NO_LIB) +#if defined(BOOST_FILESYSTEM_DYN_LINK) +#undef BOOST_FILESYSTEM_DYN_LINK +#endif + // Test that header file is self-contained. #include - #include +#include +#include + #include #include +#include +#include +#include +#include +#include - +#include namespace bpv = boost::process::v2; +namespace asio = boost::asio; + +#if defined(BOOST_PROCESS_V2_WINDOWS) +bpv::filesystem::path shell() +{ + return bpv::environment::find_executable("cmd"); +} + +bpv::filesystem::path closable() +{ + return bpv::environment::find_executable("notepad"); +} + +bpv::filesystem::path interruptable() +{ + return bpv::environment::find_executable("cmd"); +} +#else +bpv::filesystem::path shell() +{ + return bpv::environment::find_executable("sh"); +} +bpv::filesystem::path closable() +{ + return bpv::environment::find_executable("tee"); +} +bpv::filesystem::path interruptable() +{ + return bpv::environment::find_executable("tee"); +} +#endif + +BOOST_AUTO_TEST_SUITE(with_target); BOOST_AUTO_TEST_CASE(exit_code_sync) { @@ -59,5 +103,290 @@ BOOST_AUTO_TEST_CASE(exit_code_async) } +BOOST_AUTO_TEST_CASE(terminate) +{ + asio::io_context ctx; + + auto sh = shell(); + + BOOST_CHECK_MESSAGE(!sh.empty(), sh); + bpv::process proc(ctx, sh, {}); + proc.terminate(); + proc.wait(); +} + +BOOST_AUTO_TEST_CASE(request_exit) +{ + asio::io_context ctx; + + auto sh = closable(); + BOOST_CHECK_MESSAGE(!sh.empty(), sh); + bpv::process proc(ctx, sh, {} +#if defined(ASIO_WINDOWS) + , asio::windows::show_window_minimized_not_active +#endif + ); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + proc.request_exit(); + proc.wait(); +} + +BOOST_AUTO_TEST_CASE(interrupt) +{ + asio::io_context ctx; + + auto sh = interruptable(); + BOOST_CHECK_MESSAGE(!sh.empty(), sh); + bpv::process proc(ctx, sh, {} +#if defined(ASIO_WINDOWS) + , asio::windows::create_new_process_group +#endif + ); + proc.interrupt(); + proc.wait(); +} + +void trim_end(std::string & str) +{ + auto itr = std::find_if(str.rbegin(), str.rend(), [](char c) {return !std::isspace(c);}); + str.erase(itr.base(), str.end()); +} + +BOOST_AUTO_TEST_CASE(print_args_out) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + + asio::readable_pipe rp{ctx}; + asio::writable_pipe wp{ctx}; + asio::connect_pipe(rp, wp); + + + bpv::process proc(ctx, pth, {"print-args", "foo", "bar"}, bpv::process_stdio{/*in*/{},/*out*/wp, /*err*/ nullptr}); + + wp.close(); + asio::streambuf st; + std::istream is{&st}; + bpv::error_code ec; + + auto sz = asio::read(rp, st, ec); + + BOOST_CHECK_NE(sz, 0); + BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); + + std::string line; + BOOST_CHECK(std::getline(is, line)); + trim_end(line); + BOOST_CHECK_EQUAL(pth, line); + + BOOST_CHECK(std::getline(is, line)); + trim_end(line); + BOOST_CHECK_EQUAL("print-args", line); + + BOOST_CHECK(std::getline(is, line)); + trim_end(line); + BOOST_CHECK_EQUAL("foo", line); + + BOOST_CHECK(std::getline(is, line)); + trim_end(line); + BOOST_CHECK_EQUAL("bar", line); + + + proc.wait(); + BOOST_CHECK(proc.exit_code() == 0); +} + + +BOOST_AUTO_TEST_CASE(print_args_err) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + + asio::readable_pipe rp{ctx}; + asio::writable_pipe wp{ctx}; + asio::connect_pipe(rp, wp); + + bpv::process proc(ctx, pth, {"print-args", "bar", "foo"}, bpv::process_stdio{/*in*/{}, /*.out= */ nullptr, /* .err=*/ wp}); + + wp.close(); + asio::streambuf st; + std::istream is{&st}; + bpv::error_code ec; + + auto sz = asio::read(rp, st, ec); + + BOOST_CHECK_NE(sz , 0); + BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); + + std::string line; + BOOST_CHECK(std::getline(is, line)); + trim_end(line); + BOOST_CHECK_EQUAL(pth, line ); + + BOOST_CHECK(std::getline(is, line)); + trim_end(line); + BOOST_CHECK_EQUAL("print-args", line); + + BOOST_CHECK(std::getline(is, line)); + trim_end(line); + BOOST_CHECK_EQUAL("bar", line); + + BOOST_CHECK(std::getline(is, line)); + trim_end(line); + BOOST_CHECK_EQUAL("foo", line); + + + proc.wait(); + BOOST_CHECK_EQUAL(proc.exit_code(), 0); +} + +BOOST_AUTO_TEST_CASE(echo_file) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + + asio::readable_pipe rp{ctx}; + asio::writable_pipe wp{ctx}; + asio::connect_pipe(rp, wp); + + auto p = bpv::filesystem::temp_directory_path() / "asio-test-thingy.txt"; + + std::string test_data = "some ~~ test ~~ data"; + { + std::ofstream ofs{p.string()}; + ofs.write(test_data.data(), test_data.size()); + BOOST_CHECK(ofs); + } + + bpv::process proc(ctx, pth, {"echo"}, bpv::process_stdio{/*.in=*/p, /*.out=*/wp}); + wp.close(); + + std::string out; + bpv::error_code ec; + + auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); + BOOST_CHECK(sz != 0); + BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); + BOOST_CHECK_MESSAGE(out == test_data, out); + + proc.wait(); + BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code()); +} + +BOOST_AUTO_TEST_CASE(print_same_cwd) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + + asio::readable_pipe rp{ctx}; + asio::writable_pipe wp{ctx}; + asio::connect_pipe(rp, wp); + + + // default CWD + bpv::process proc(ctx, pth, {"print-cwd"}, bpv::process_stdio{/*.in=*/{},/*.out=*/wp}); + wp.close(); + + std::string out; + bpv::error_code ec; + + auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); + BOOST_CHECK(sz != 0); + BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); + BOOST_CHECK_MESSAGE(bpv::filesystem::path(out) == bpv::filesystem::current_path(), + bpv::filesystem::path(out) << " != " << bpv::filesystem::current_path()); + + proc.wait(); + BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code()); +} + +BOOST_AUTO_TEST_CASE(print_other_cwd) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + + asio::readable_pipe rp{ctx}; + asio::writable_pipe wp{ctx}; + asio::connect_pipe(rp, wp); + + auto tmp = bpv::filesystem::canonical(bpv::filesystem::temp_directory_path()); + + // default CWD + bpv::process proc(ctx, pth, {"print-cwd"}, bpv::process_stdio{/*.in=*/{}, /*.out=*/wp}, bpv::process_start_dir(tmp)); + wp.close(); + + std::string out; + bpv::error_code ec; + + auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); + BOOST_CHECK(sz != 0); + BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); + BOOST_CHECK_MESSAGE(bpv::filesystem::path(out) == tmp, + bpv::filesystem::path(out) << " != " << tmp); + + proc.wait(); + BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code() << " from " << proc.native_exit_code()); +} + + +template +std::string read_env(const char * name, Inits && ... inits) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + + asio::readable_pipe rp{ctx}; + asio::writable_pipe wp{ctx}; + asio::connect_pipe(rp, wp); + + bpv::process proc(ctx, pth, {"print-env", name}, bpv::process_stdio{/*.in-*/{}, /*.out*/{wp}}, std::forward(inits)...); + + wp.close(); + + std::string out; + bpv::error_code ec; + + auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); + BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); + + printf("FOOBAR %lld '%s'\n", sz, out.c_str()); + trim_end(out); + + proc.wait(); + BOOST_CHECK_EQUAL(proc.exit_code(), 0); + + return out; +} + +BOOST_AUTO_TEST_CASE(environment) +{ + BOOST_CHECK_EQUAL(read_env("PATH"), ::getenv("PATH")); + + BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{"FOOBAR=FOO-BAR"})); + BOOST_CHECK_EQUAL("BAR-FOO", read_env("PATH", bpv::process_environment{"PATH=BAR-FOO", "XYZ=ZYX"})); + BOOST_CHECK_EQUAL("BAR-FOO", read_env("PATH", bpv::process_environment{"PATH=BAR-FOO", "XYZ=ZYX"})); + +#if defined(BOOST_PROCESS_V2_WINDOWS) + BOOST_CHECK_EQUAL("BAR-FOO", read_env("PATH", bpv::process_environment{L"PATH=BAR-FOO", L"XYZ=ZYX"})); + BOOST_CHECK_EQUAL("BAR-FOO", read_env("PATH", bpv::process_environment{L"PATH=BAR-FOO", L"XYZ=ZYX"})); + BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{L"FOOBAR=FOO-BAR"})); +#endif + + BOOST_CHECK_EQUAL(read_env("PATH", bpv::process_environment(bpv::environment::current())), ::getenv("PATH")); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/v2/target.cpp b/test/v2/target.cpp index bde8fbfc8..ea24a85aa 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -1,11 +1,50 @@ - +#include #include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#endif int main(int argc, char * argv[]) { std::string mode = argv[1]; if (mode == "exit-code") return std::stoi(argv[2]); + else if (mode == "print-args") + for (auto i = 0; i < argc; i++) + { + std::cout << argv[i] << std::endl; + std::cerr << argv[i] << std::endl; + if (!std::cout || !std::cerr) + return 1; + + } + else if (mode == "echo") + std::cout << std::cin.rdbuf(); + else if (mode == "print-cwd") + { +#if defined(BOOST_PROCESS_V2_WINDOWS) + wchar_t buf[65535]; + const auto sz = ::GetCurrentDirectoryW(sizeof(buf), buf); + std::wcout << boost::process::v2::wstring_view(buf, sz) << std::flush; +#else +#endif + } + else if (mode == "check-eof") + { + std::string st; + std::cin >> st; + return std::cin.eof() ? 0 : 1; + } + else if (mode == "print-env") + { + auto p = ::getenv(argv[2]); + assert(printf("%s", p) > 0); + } + else + return 34; + return 0; } \ No newline at end of file From 257da990d5cae4bc25768b3ab829d109d44931b1 Mon Sep 17 00:00:00 2001 From: Klemens Date: Mon, 30 May 2022 01:41:20 +0800 Subject: [PATCH 058/397] Added pidfd_open impl for linux. --- include/boost/process/v2/default_launcher.hpp | 14 +- include/boost/process/v2/detail/config.hpp | 17 + .../v2/detail/impl/process_handle_windows.ipp | 6 +- .../process/v2/detail/process_handle_fd.hpp | 289 ++++++++++ .../v2/detail/process_handle_fd_or_signal.hpp | 295 +++++++++++ .../v2/detail/process_handle_signal.hpp | 286 ++++++++++ .../v2/detail/process_handle_windows.hpp | 400 +++++++------- include/boost/process/v2/environment.hpp | 53 +- include/boost/process/v2/posix/bind_fd.hpp | 57 ++ .../process/v2/posix/default_launcher.hpp | 494 ++++++++++++++++++ .../v2/posix/fork_and_forget_launcher.hpp | 132 +++++ .../process/v2/posix/pdfork_launcher.hpp | 164 ++++++ .../boost/process/v2/posix/vfork_launcher.hpp | 133 +++++ include/boost/process/v2/process.hpp | 27 +- include/boost/process/v2/process_handle.hpp | 34 ++ include/boost/process/v2/start_dir.hpp | 2 +- test/v2/CMakeLists.txt | 17 +- test/v2/process.cpp | 4 +- test/v2/target.cpp | 6 +- 19 files changed, 2186 insertions(+), 244 deletions(-) create mode 100644 include/boost/process/v2/detail/process_handle_fd.hpp create mode 100644 include/boost/process/v2/detail/process_handle_fd_or_signal.hpp create mode 100644 include/boost/process/v2/detail/process_handle_signal.hpp create mode 100644 include/boost/process/v2/posix/bind_fd.hpp create mode 100644 include/boost/process/v2/posix/default_launcher.hpp create mode 100644 include/boost/process/v2/posix/fork_and_forget_launcher.hpp create mode 100644 include/boost/process/v2/posix/pdfork_launcher.hpp create mode 100644 include/boost/process/v2/posix/vfork_launcher.hpp diff --git a/include/boost/process/v2/default_launcher.hpp b/include/boost/process/v2/default_launcher.hpp index 3dee6ed73..819129aae 100644 --- a/include/boost/process/v2/default_launcher.hpp +++ b/include/boost/process/v2/default_launcher.hpp @@ -15,6 +15,13 @@ #if defined(BOOST_PROCESS_V2_WINDOWS) #include +#else +#if defined(BOOST_PROCESS_V2_PDFORK) +#include +#else +#include +#endif + #endif BOOST_PROCESS_V2_BEGIN_NAMESPACE @@ -22,8 +29,11 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE #if defined(BOOST_PROCESS_V2_WINDOWS) typedef windows::default_launcher default_process_launcher; #else - - +#if defined(BOOST_PROCESS_V2_PDFORK) +typedef posix::pdfork_launcher default_process_launcher; +#else +typedef posix::default_launcher default_process_launcher; +#endif #endif diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp index 59708cc58..dabbca99d 100644 --- a/include/boost/process/v2/detail/config.hpp +++ b/include/boost/process/v2/detail/config.hpp @@ -130,4 +130,21 @@ BOOST_PROCESS_V2_END_NAMESPACE # define BOOST_PROCESS_V2_DECL #endif +#if defined(BOOST_PROCESS_V2_POSIX) + +#if defined(__linux__) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN) +#define BOOST_PROCESS_V2_PIDFD_OPEN 1 +#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1 +#endif + +#if defined(__FreeBSD__) && !defined(BOOST_PROCESS_V2_DISABLE_PDFORK) +#define BOOST_PROCESS_V2_PDFORK 1 +#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1 +#endif +#else +#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1 +#endif + + + #endif //BOOST_PROCESS_V2_DETAIL_CONFIG_HPP diff --git a/include/boost/process/v2/detail/impl/process_handle_windows.ipp b/include/boost/process/v2/detail/impl/process_handle_windows.ipp index 293abb085..bbc324bb8 100644 --- a/include/boost/process/v2/detail/impl/process_handle_windows.ipp +++ b/include/boost/process/v2/detail/impl/process_handle_windows.ipp @@ -113,12 +113,14 @@ void check_running_(HANDLE handle, error_code & ec, DWORD & exit_status) ec = detail::get_last_error(); } -} #if !defined(BOOST_PROCESS_V2_HEADER_ONLY) -template struct basic_process_handle<>; +template struct basic_process_handle_win<>; #endif +} + + BOOST_PROCESS_V2_END_NAMESPACE #endif //BOOST_PROCESS_V2_DETAIL_IMPL_PROCESS_HANDLE_WINDOWS_IPP diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp new file mode 100644 index 000000000..1ae5877b2 --- /dev/null +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -0,0 +1,289 @@ + +// 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_DETAIL_PROCESS_HANDLE_FD_HPP +#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_HPP + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +template +struct basic_process_handle_fd +{ + using native_handle_type = int; + + typedef Executor executor_type; + + executor_type get_executor() + { return descriptor_.get_executor(); } + + /// Rebinds the process_handle to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_process_handle_fd other; + }; + + template + basic_process_handle_fd(ExecutionContext &context, + typename std::enable_if< + std::is_convertible::value + >::type = 0) + : pid_(-1), descriptor_(context) + { + } + + basic_process_handle_fd(Executor executor) + : pid_(-1), descriptor_(executor) + { + } + + basic_process_handle_fd(Executor executor, pid_type pid) + : pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0)) + { + } + + basic_process_handle_fd(Executor executor, pid_type pid, native_handle_type process_handle) + : pid_(pid), descriptor_(executor, process_handle) + { + } + + template + basic_process_handle_fd(basic_process_handle_fd &&handle) + : pid_(handle.pid_), descriptor_(std::move(handle.descriptor_)) + { + } + + pid_type id() const + { return pid_; } + + void terminate_if_running(error_code &) + { + if (pid_ <= 0) + return; + if (::waitpid(pid_, nullptr, WNOHANG) == 0) + { + ::kill(pid_, SIGKILL); + ::waitpid(pid_, nullptr, 0); + } + } + + void terminate_if_running() + { + if (pid_ <= 0) + return; + if (::waitpid(pid_, nullptr, WNOHANG) == 0) + { + ::kill(pid_, SIGKILL); + ::waitpid(pid_, nullptr, 0); + } + } + + void wait(native_exit_code_type &exit_status, error_code &ec) + { + if (pid_ <= 0) + return; + if (::waitpid(pid_, &exit_status, 0) == 1) + ec = get_last_error(); + } + + void wait(native_exit_code_type &exit_status) + { + if (pid_ <= 0) + return; + error_code ec; + wait(exit_status, ec); + if (ec) + detail::throw_error(ec, "wait(pid)"); + } + + void interrupt(error_code &ec) + { + if (pid_ <= 0) + return; + if (::kill(pid_, SIGINT) == -1) + ec = get_last_error(); + } + + void interrupt() + { + if (pid_ <= 0) + return; + error_code ec; + interrupt(ec); + if (ec) + detail::throw_error(ec, "interrupt"); + } + + void request_exit(error_code &ec) + { + if (pid_ <= 0) + return; + if (::kill(pid_, SIGTERM) == -1) + ec = get_last_error(); + } + + void request_exit() + { + if (pid_ <= 0) + return; + error_code ec; + request_exit(ec); + if (ec) + detail::throw_error(ec, "request_exit"); + } + + void terminate(native_exit_code_type &exit_status, error_code &ec) + { + if (pid_ <= 0) + return; + if (::kill(pid_, SIGKILL) == -1) + ec = get_last_error(); + } + + void terminate(native_exit_code_type &exit_status) + { + if (pid_ <= 0) + return; + error_code ec; + terminate(exit_status, ec); + if (ec) + detail::throw_error(ec, "terminate"); + } + + bool running(native_exit_code_type &exit_code, error_code ec) + { + if (pid_ <= 0) + return false; + int code = 0; + int res = ::waitpid(pid_, &code, 0); + if (res == -1) + ec = get_last_error(); + else + ec.clear(); + + if (process_is_running(res)) + return true; + else + { + exit_code = code; + return false; + } + } + + bool running(native_exit_code_type &exit_code) + { + if (pid_ <= 0) + return false; + + error_code ec; + bool res = running(exit_code, ec); + if (ec) + detail::throw_error(ec, "is_running"); + return res; + } + + bool is_open() const + { + return pid_ != -1; + } + + template + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) + async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( + async_wait_op_{descriptor_, pid_}, handler, descriptor_); + } + + private: + template + friend + struct basic_process_handle_fd; + pid_type pid_ = -1; + BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_stream_descriptor descriptor_; + + struct async_wait_op_ + { + BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_descriptor &descriptor; + pid_type pid_; + + template + void operator()(Self &&self) + { + error_code ec; + native_exit_code_type exit_code; + if (pid_ <= 0) // error, complete early + ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; + else if (::waitpid(pid_, &exit_code, 0) == -1) + ec = get_last_error(); + + if (!ec && process_is_running(exit_code)) + { + descriptor.async_wait( + BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::descriptor_base::wait_read, 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); + } + }; + BOOST_PROCESS_V2_ASIO_NAMESPACE::post(descriptor.get_executor(), + completer{ec, exit_code, std::move(self)}); + + } + + template + void operator()(Self &&self, error_code ec, int = 0) + { + native_exit_code_type exit_code; + if (!ec) + if (::waitpid(pid_, &exit_code, 0) == -1) + ec = get_last_error(); + std::move(self).complete(ec, exit_code); + } + }; +}; +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP 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 new file mode 100644 index 000000000..c8224b65f --- /dev/null +++ b/include/boost/process/v2/detail/process_handle_fd_or_signal.hpp @@ -0,0 +1,295 @@ + +// 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_DETAIL_PROCESS_HANDLE_FD_OR_SIGNAL_HPP +#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_OR_SIGNAL_HPP + +#include + +#include +#include + +#include +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +template +struct basic_process_handle_fd_or_signal +{ + using native_handle_type = int; + + typedef Executor executor_type; + + executor_type get_executor() + { return signal_set_.get_executor(); } + + /// Rebinds the process_handle to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_process_handle_fd_or_signal other; + }; + + template + basic_process_handle_fd_or_signal(ExecutionContext &context, + typename std::enable_if< + std::is_convertible::value + >::type = 0) + : pid_(-1), descriptor_(context) + { + } + + basic_process_handle_fd_or_signal(Executor executor) + : pid_(-1), descriptor_(executor) + { + } + + basic_process_handle_fd_or_signal(Executor executor, pid_type pid) + : pid_(pid), descriptor_(executor) + { + } + + basic_process_handle_fd_or_signal(Executor executor, pid_type pid, native_handle_type process_handle) + : pid_(pid), descriptor_(executor, process_handle) + { + } + + template + basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal &&handle) + : pid_(handle.pid_), descriptor_(std::move(handle.descriptor_)) + { + } + + pid_type id() const + { return pid_; } + + void terminate_if_running(error_code &) + { + if (pid_ <= 0) + return; + if (::waitpid(pid_, nullptr, WNOHANG) == 0) + { + ::kill(pid_, SIGKILL); + ::waitpid(pid_, nullptr, 0); + } + } + + void terminate_if_running() + { + if (pid_ <= 0) + return; + if (::waitpid(pid_, nullptr, WNOHANG) == 0) + { + ::kill(pid_, SIGKILL); + ::waitpid(pid_, nullptr, 0); + } + } + + void wait(native_exit_code_type &exit_status, error_code &ec) + { + if (pid_ <= 0) + return; + if (::waitpid(pid_, &exit_status, 0) == 1) + ec = get_last_error(); + } + + void wait(native_exit_code_type &exit_status) + { + if (pid_ <= 0) + return; + error_code ec; + wait(exit_status, ec); + if (ec) + detail::throw_error(ec, "wait(pid)"); + } + + void interrupt(error_code &ec) + { + if (pid_ <= 0) + return; + if (::kill(pid_, SIGINT) == -1) + ec = get_last_error(); + } + + void interrupt() + { + if (pid_ <= 0) + return; + error_code ec; + interrupt(ec); + if (ec) + detail::throw_error(ec, "interrupt"); + } + + void request_exit(error_code &ec) + { + if (pid_ <= 0) + return; + if (::kill(pid_, SIGTERM) == -1) + ec = get_last_error(); + } + + void request_exit() + { + if (pid_ <= 0) + return; + error_code ec; + request_exit(ec); + if (ec) + detail::throw_error(ec, "request_exit"); + } + + void terminate(native_exit_code_type &exit_status, error_code &ec) + { + if (pid_ <= 0) + return; + if (::kill(pid_, SIGKILL) == -1) + ec = get_last_error(); + } + + void terminate(native_exit_code_type &exit_status) + { + if (pid_ <= 0) + return; + error_code ec; + terminate(exit_status, ec); + if (ec) + detail::throw_error(ec, "terminate"); + } + + bool running(native_exit_code_type &exit_code, error_code ec) + { + if (pid_ <= 0) + return false; + int code = 0; + int res = ::waitpid(pid_, &code, 0); + if (res == -1) + ec = get_last_error(); + else + ec.clear(); + + if (process_is_running(res)) + return true; + else + { + exit_code = code; + return false; + } + } + + bool running(native_exit_code_type &exit_code) + { + if (pid_ <= 0) + return false; + + error_code ec; + bool res = running(exit_code, ec); + if (ec) + detail::throw_error(ec, "is_running"); + return res; + } + + bool is_open() const + { + return pid_ != -1; + } + + template + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) + async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( + async_wait_op_{descriptor_, signal_set_, pid_}, handler, descriptor_); + } + + private: + template + friend + struct basic_process_handle_fd_or_signal; + pid_type pid_ = -1; + BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_stream_descriptor descriptor_; + BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set signal_set_{descriptor_.get_executor(), SIGCHLD}; + + struct async_wait_op_ + { + BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_descriptor &descriptor; + BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set &handle; + pid_type pid_; + + template + void operator()(Self &&self) + { + error_code ec; + native_exit_code_type exit_code; + if (pid_ <= 0) // error, complete early + ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; + else if (::waitpid(pid_, &exit_code, 0) == -1) + ec = get_last_error(); + + if (!ec && process_is_running(exit_code)) + { + if (descriptor.is_open()) + descriptor.async_wait( + BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::descriptor_base::wait_read, std::move(self)); + 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); + } + }; + BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(), + completer{ec, exit_code, std::move(self)}); + + } + + template + void operator()(Self &&self, error_code ec, int = 0) + { + native_exit_code_type exit_code; + if (!ec) + if (::waitpid(pid_, &exit_code, 0) == -1) + ec = get_last_error(); + std::move(self).complete(ec, exit_code); + } + }; +}; +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp new file mode 100644 index 000000000..1f766d3ac --- /dev/null +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -0,0 +1,286 @@ +// 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_DETAIL_PROCESS_HANDLE_SIGNAL_HPP +#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP + +#include + +#include +#include + +#include +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace detail +{ + +template +struct basic_process_handle_signal +{ + struct native_handle_type + { + native_handle_type() = delete; + native_handle_type(const native_handle_type & ) = delete; + ~native_handle_type() = delete; + }; + + typedef Executor executor_type; + + executor_type get_executor() + { return signal_set_.get_executor(); } + + /// Rebinds the process_handle to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_process_handle_signal other; + }; + + template + basic_process_handle_signal(ExecutionContext &context, + typename std::enable_if< + std::is_convertible::value + >::type = 0) + : pid_(-1), signal_set_(context, SIGCHLD) + { + } + + basic_process_handle_signal(Executor executor) + : pid_(-1), signal_set_(executor, SIGCHLD) + { + } + + basic_process_handle_signal(Executor executor, pid_type pid) + : pid_(pid), signal_set_(executor, SIGCHLD) + { + } + + template + basic_process_handle_signal(basic_process_handle_signal && handle) + : pid_(handle.pid_), signal_set_(Executor1(handle.signal_set_.get_executor()), SIGCHLD) + { + } + + pid_type id() const + { return pid_; } + + void terminate_if_running(error_code &) + { + if (pid_ <= 0) + return ; + if (::waitpid(pid_, nullptr, WNOHANG) == 0) + { + ::kill(pid_, SIGKILL); + ::waitpid(pid_, nullptr, 0); + } + } + + void terminate_if_running() + { + if (pid_ <= 0) + return ; + if (::waitpid(pid_, nullptr, WNOHANG) == 0) + { + ::kill(pid_, SIGKILL); + ::waitpid(pid_, nullptr, 0); + } + } + + void wait(native_exit_code_type &exit_status, error_code &ec) + { + if (pid_ <= 0) + return ; + if (::waitpid(pid_, &exit_status, 0) == 1) + ec = get_last_error(); + } + + void wait(native_exit_code_type &exit_status) + { + if (pid_ <= 0) + return ; + error_code ec; + wait(exit_status, ec); + if (ec) + detail::throw_error(ec, "wait(pid)"); + } + + void interrupt(error_code &ec) + { + if (pid_ <= 0) + return ; + if (::kill(pid_, SIGINT) == -1) + ec = get_last_error(); + } + + void interrupt() + { + if (pid_ <= 0) + return ; + error_code ec; + interrupt(ec); + if (ec) + detail::throw_error(ec, "interrupt"); + } + + void request_exit(error_code &ec) + { + if (pid_ <= 0) + return ; + if (::kill(pid_, SIGTERM) == -1) + ec = get_last_error(); + } + + void request_exit() + { + if (pid_ <= 0) + return ; + error_code ec; + request_exit(ec); + if (ec) + detail::throw_error(ec, "request_exit"); + } + + void terminate(native_exit_code_type &exit_status, error_code &ec) + { + if (pid_ <= 0) + return ; + if (::kill(pid_, SIGKILL) == -1) + ec = get_last_error(); + } + + void terminate(native_exit_code_type &exit_status) + { + if (pid_ <= 0) + return ; + error_code ec; + terminate(exit_status, ec); + if (ec) + detail::throw_error(ec, "terminate"); + } + + bool running(native_exit_code_type &exit_code, error_code ec) + { + if (pid_ <= 0) + return false; + int code = 0; + int res = ::waitpid(pid_, &code, 0); + if (res == -1) + ec = get_last_error(); + else + ec.clear(); + + if (process_is_running(res)) + return true; + else + { + exit_code = code; + return false; + } + } + + bool running(native_exit_code_type &exit_code) + { + if (pid_ <= 0) + return false; + + error_code ec; + bool res = running(exit_code, ec); + if (ec) + detail::throw_error(ec, "is_running"); + return res; + } + + bool is_open() const + { + return pid_ != -1; + } + + template + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) + async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( + async_wait_op_{signal_set_, pid_}, handler, signal_set_); + } + + private: + template + friend struct basic_process_handle_signal; + pid_type pid_ = -1; + BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set signal_set_; + + struct async_wait_op_ + { + BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set &handle; + pid_type pid_; + template + void operator()(Self &&self) + { + error_code ec; + native_exit_code_type exit_code; + if (pid_ <= 0) // error, complete early + ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; + else if (::waitpid(pid_, &exit_code, 0) == -1) + ec = get_last_error(); + + if (!ec && process_is_running(exit_code)) + { + 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); + } + }; + BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(), + completer{ec, exit_code, std::move(self)}); + + } + + template + void operator()(Self &&self, error_code ec, int ) + { + native_exit_code_type exit_code; + if (!ec) + if (::waitpid(pid_, &exit_code, 0) == -1) + ec = get_last_error(); + std::move(self).complete(ec, exit_code); + } + }; +}; + +} + + +BOOST_PROCESS_V2_END_NAMESPACE + + +#endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp index 3f3f87bae..b4e4d8ff9 100644 --- a/include/boost/process/v2/detail/process_handle_windows.hpp +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -12,7 +12,7 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) #include -#include +#include #include #else #include @@ -35,208 +35,218 @@ BOOST_PROCESS_V2_DECL void terminate_(void * handle, error_code & ec, native_exi BOOST_PROCESS_V2_DECL void request_exit_(pid_type pid_, error_code & ec); BOOST_PROCESS_V2_DECL void check_running_(void* handle, error_code & ec, native_exit_code_type & exit_status); -} - template -struct basic_process_handle +struct basic_process_handle_win { - typedef BOOST_PROCESS_V2_ASIO_NAMESPACE::windows::basic_object_handle handle_type; - typedef typename handle_type::native_handle_type native_handle_type; + typedef BOOST_PROCESS_V2_ASIO_NAMESPACE::windows::basic_object_handle handle_type; + typedef typename handle_type::native_handle_type native_handle_type; - typedef Executor executor_type; - executor_type get_executor() {return handle_.get_executor();} - - /// Rebinds the process_handle to another executor. - template - struct rebind_executor - { - /// The socket type when rebound to the specified executor. - typedef basic_process_handle other; - }; - - template - basic_process_handle(ExecutionContext &context, - typename std::enable_if< - std::is_convertible::value - >::type = 0) - : pid_(0), handle_(context) - { - } - - basic_process_handle(Executor executor) - : pid_(0), handle_(executor) - { - } - - basic_process_handle(Executor executor, pid_type pid) - : pid_(pid), handle_(executor, detail::open_process_(pid)) - { - } - - basic_process_handle(Executor executor, pid_type pid, native_handle_type process_handle) - : pid_(pid), handle_(executor, process_handle) - { - } - - native_handle_type native_handle() {return handle_.native_handle();} - pid_type id() const {return pid_;} - - void terminate_if_running(error_code & ) - { - detail::terminate_if_running_(handle_.native_handle()); - } - - void terminate_if_running() - { - detail::terminate_if_running_(handle_.native_handle()); - } - - void wait(native_exit_code_type & exit_status, error_code & ec) - { - if (!detail::check_handle_(handle_.native_handle(), ec)) - return; - - handle_.wait(ec); - if (!ec) - detail::get_exit_code_(handle_.native_handle(), exit_status, ec); - } - - - void wait(native_exit_code_type & exit_status) - { - error_code ec; - wait(exit_status, ec); - if (ec) - detail::throw_error(ec, "wait(pid)"); - } - - void interrupt(error_code & ec) - { - if (!detail::check_pid_(pid_, ec)) - return; - - detail::interrupt_(pid_, ec); - } - - void interrupt() - { - error_code ec; - interrupt(ec); - if (ec) - detail::throw_error(ec, "interrupt"); - } - - void request_exit(error_code & ec) - { - if (!detail::check_pid_(pid_, ec)) - return ; - - detail::request_exit_(pid_, ec); - } - - void request_exit() - { - error_code ec; - request_exit(ec); - if (ec) - detail::throw_error(ec, "request_exit"); - } - - void terminate(native_exit_code_type & exit_status, error_code & ec) - { - if (!detail::check_handle_(handle_.native_handle(), ec)) - return; - - detail::terminate_(handle_.native_handle(), ec, exit_status); - if (!ec) - wait(exit_status, ec); - - } - - void terminate(native_exit_code_type & exit_status) - { - error_code ec; - terminate(exit_status, ec); - if (ec) - detail::throw_error(ec, "terminate"); - } - - bool running(native_exit_code_type & exit_code, error_code ec) - { - if (!detail::check_handle_(handle_.native_handle(), ec)) - return false; - - native_exit_code_type code; - //single value, not needed in the winapi. - detail::check_running_(handle_.native_handle(), ec, code); - if (ec) - return false; - - if (process_is_running(code)) - return true; - else - { - exit_code = code; - return false; - } - } - - bool running(native_exit_code_type & exit_code) - { - error_code ec; - bool res = running(exit_code, ec); - if (ec) - detail::throw_error(ec, "is_running"); - return res; - } - - bool is_open() const - { - return handle_.is_open(); - } - - template - BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) - async_wait(WaitHandler && handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) - { - return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( - async_wait_op_{handle_}, handler, handle_ - ); - } - - private: - pid_type pid_; - handle_type handle_; - - struct async_wait_op_ - { - handle_type & handle; - - template - void operator()(Self && self) - { - handle.async_wait(std::move(self)); - } - - template - void operator()(Self && self, error_code ec) - { - native_exit_code_type exit_code; - if (!ec) - detail::get_exit_code_(handle.native_handle(), exit_code, ec); - std::move(self).complete(ec, exit_code); - } - }; -}; + typedef Executor executor_type; + + executor_type get_executor() + { return handle_.get_executor(); } + + /// Rebinds the process_handle to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_process_handle_win other; + }; + + template + basic_process_handle_win(ExecutionContext &context, + typename std::enable_if< + std::is_convertible::value + >::type = 0) + : pid_(0), handle_(context) + { + } + + basic_process_handle_win(Executor executor) + : pid_(0), handle_(executor) + { + } + + basic_process_handle_win(Executor executor, pid_type pid) + : pid_(pid), handle_(executor, detail::open_process_(pid)) + { + } + + basic_process_handle_win(Executor executor, pid_type pid, native_handle_type process_handle) + : pid_(pid), handle_(executor, process_handle) + { + } + + template + basic_process_handle_win(basic_process_handle_win && handle) + : pid_(handle.pid_), handle_(std::move(handle).handle_) + { + } + + native_handle_type native_handle() + { return handle_.native_handle(); } + + pid_type id() const + { return pid_; } + + void terminate_if_running(error_code &) + { + detail::terminate_if_running_(handle_.native_handle()); + } + + void terminate_if_running() + { + detail::terminate_if_running_(handle_.native_handle()); + } + + void wait(native_exit_code_type &exit_status, error_code &ec) + { + if (!detail::check_handle_(handle_.native_handle(), ec)) + return; + + handle_.wait(ec); + if (!ec) + detail::get_exit_code_(handle_.native_handle(), exit_status, ec); + } + + + void wait(native_exit_code_type &exit_status) + { + error_code ec; + wait(exit_status, ec); + if (ec) + detail::throw_error(ec, "wait(pid)"); + } -using process_handle = basic_process_handle<>; + void interrupt(error_code &ec) + { + if (!detail::check_pid_(pid_, ec)) + return; + + detail::interrupt_(pid_, ec); + } + + void interrupt() + { + error_code ec; + interrupt(ec); + if (ec) + detail::throw_error(ec, "interrupt"); + } + + void request_exit(error_code &ec) + { + if (!detail::check_pid_(pid_, ec)) + return; + + detail::request_exit_(pid_, ec); + } + + void request_exit() + { + error_code ec; + request_exit(ec); + if (ec) + detail::throw_error(ec, "request_exit"); + } + + void terminate(native_exit_code_type &exit_status, error_code &ec) + { + if (!detail::check_handle_(handle_.native_handle(), ec)) + return; + + detail::terminate_(handle_.native_handle(), ec, exit_status); + if (!ec) + wait(exit_status, ec); + + } + + void terminate(native_exit_code_type &exit_status) + { + error_code ec; + terminate(exit_status, ec); + if (ec) + detail::throw_error(ec, "terminate"); + } + + bool running(native_exit_code_type &exit_code, error_code ec) + { + if (!detail::check_handle_(handle_.native_handle(), ec)) + return false; + + native_exit_code_type code; + //single value, not needed in the winapi. + detail::check_running_(handle_.native_handle(), ec, code); + if (ec) + return false; + + if (process_is_running(code)) + return true; + else + { + exit_code = code; + return false; + } + } + + bool running(native_exit_code_type &exit_code) + { + error_code ec; + bool res = running(exit_code, ec); + if (ec) + detail::throw_error(ec, "is_running"); + return res; + } + + bool is_open() const + { + return handle_.is_open(); + } + + template + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) + async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( + async_wait_op_{handle_}, handler, handle_ + ); + } + template + friend struct basic_process_handle_win; + private: + pid_type pid_; + handle_type handle_; + + struct async_wait_op_ + { + handle_type &handle; + + template + void operator()(Self &&self) + { + handle.async_wait(std::move(self)); + } + + template + void operator()(Self &&self, error_code ec) + { + native_exit_code_type exit_code; + if (!ec) + detail::get_exit_code_(handle.native_handle(), exit_code, ec); + std::move(self).complete(ec, exit_code); + } + }; +}; #if !defined(BOOST_PROCESS_V2_HEADER_ONLY) -extern template struct basic_process_handle<>; +extern template struct basic_process_handle_win<>; #endif +} + BOOST_PROCESS_V2_END_NAMESPACE diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index f809c106f..619d697d2 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -64,10 +64,8 @@ using native_handle = implementation-defined; struct value_iterator { using string_view_type = basic_string_view>; - using difference_type = std::size_t; - using value_type = string_view_type; - using pointer = const string_view_type *; - using reference = const string_view_type & ; + using difference_type = int; + using reference = string_view_type; using iterator_category = std::forward_iterator_tag; value_iterator & operator++() @@ -94,11 +92,6 @@ struct value_iterator else return view_.substr(0, delim); } - const string_view_type operator->() const - { - return **this; - } - value_iterator() = default; value_iterator(const value_iterator & ) = default; @@ -108,10 +101,6 @@ struct value_iterator friend bool operator==(const value_iterator & l, const value_iterator & r) { return l.view_ == r.view_; } friend bool operator!=(const value_iterator & l, const value_iterator & r) { return l.view_ != r.view_; } - friend bool operator<=(const value_iterator & l, const value_iterator & r) { return l.view_ <= r.view_; } - friend bool operator>=(const value_iterator & l, const value_iterator & r) { return l.view_ >= r.view_; } - friend bool operator< (const value_iterator & l, const value_iterator & r) { return l.view_ < r.view_; } - friend bool operator> (const value_iterator & l, const value_iterator & r) { return l.view_ > r.view_; } private: string_view_type view_; @@ -1181,7 +1170,10 @@ struct current_view struct iterator { - using value_type = key_value_pair_view; + using value_type = key_value_pair_view; + using difference_type = int; + using reference = key_value_pair_view; + using pointer = key_value_pair_view; using iterator_category = std::forward_iterator_tag; iterator() = default; @@ -1200,20 +1192,13 @@ struct current_view iterator_ = detail::next(iterator_); return last; } - - - const key_value_pair_view operator*() const - { - return key_value_pair_view(detail::dereference(iterator_)); - } - - optional operator->() const + key_value_pair_view operator*() const { - return key_value_pair_view(detail::dereference(iterator_)); + return detail::dereference(iterator_); } - friend bool operator==(const iterator & l, const iterator & r) {return l.iterator_ == r.iterator_;} - friend bool operator!=(const iterator & l, const iterator & r) {return l.iterator_ != r.iterator_;} + friend bool operator==(const iterator & l, const iterator & r) {return l.iterator_ == r.iterator_;} + friend bool operator!=(const iterator & l, const iterator & r) {return l.iterator_ != r.iterator_;} private: environment::native_iterator iterator_; @@ -1273,7 +1258,7 @@ inline boost::process::v2::filesystem::path find_executable( return false; }); if (itr != nullptr) - return itr->value(); + return (*itr).value(); else return value_view(); }; @@ -1298,7 +1283,7 @@ inline boost::process::v2::filesystem::path find_executable( auto path = find_key("PATH"); for (auto pp_view : path) { - auto p = boost::process::v2::filesystem::path(pp_view) / name; + auto p = boost::process::v2::filesystem::path(pp_view.begin(), pp_view.end()) / name; error_code ec; bool is_exec = detail::is_executable(p, ec); if (!ec && is_exec) @@ -1558,10 +1543,10 @@ struct process_environment template static std::vector build_env(Args && args, - typename enable_if< + typename std::enable_if< std::is_convertible< decltype(*std::begin(std::declval())), - ASIO_CSTRING_VIEW>::value>::type * = nullptr) + cstring_ref>::value>::type * = nullptr) { std::vector env; for (auto && e : args) @@ -1573,16 +1558,16 @@ struct process_environment template std::vector build_env(Args && args, - typename enable_if< + typename std::enable_if< !std::is_convertible< decltype(*std::begin(std::declval())), - ASIO_CSTRING_VIEW>::value>::type * = nullptr) + cstring_ref>::value>::type * = nullptr) { std::vector env; using char_type = typename decay()))[0])>::type; - for (ASIO_BASIC_STRING_VIEW_PARAM(char_type) arg : args) - env_buffer.push_back(detail::convert_chars(arg.data(), arg.data() + arg.size(), ' ')); + for (basic_string_view arg : args) + env_buffer.push_back(detail::conv_string(arg.data(), arg.size())); for (auto && e : env_buffer) env.push_back(e.c_str()); @@ -1602,8 +1587,8 @@ struct process_environment error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *); - std::vector env; std::vector env_buffer; + std::vector env; #endif diff --git a/include/boost/process/v2/posix/bind_fd.hpp b/include/boost/process/v2/posix/bind_fd.hpp new file mode 100644 index 000000000..fe6cc069b --- /dev/null +++ b/include/boost/process/v2/posix/bind_fd.hpp @@ -0,0 +1,57 @@ +// 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_BIND_FD_HPP +#define BOOST_PROCESS_V2_POSIX_BIND_FD_HPP + +#include +#include + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace posix +{ + +struct bind_fd +{ + int target; + int fd; + bool fd_needs_closing{false}; + + ~bind_fd() + { + if (fd_needs_closing) + ::close(fd); + } + + bind_fd() = delete; + bind_fd(int target) : target(target), fd(target) {} + template + bind_fd(Stream && str, decltype(std::declval().native_handle()) = -1) + : bind_fd(str.native_handle()) + {} + bind_fd(int target, FILE * f) : bind_fd(target, fileno(f)) {} + bind_fd(int target, int fd) : target(target), fd(fd) {} + bind_fd(int target, std::nullptr_t) : bind_fd(target, filesystem::path("/dev/null")) {} + bind_fd(int target, const filesystem::path & pth, int flags = O_RDWR | O_CREAT) + : target(target), fd(::open(pth.c_str(), flags, 0660)), fd_needs_closing(true) + { + } + + error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *) + { + if (::dup2(fd, target) == -1) + return error_code(errno, system_category()); + else + return error_code (); + } +}; + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_POSIX_BIND_FD_HPP diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp new file mode 100644 index 000000000..de045dc11 --- /dev/null +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -0,0 +1,494 @@ +// 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_DEFAULT_LAUNCHER +#define BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER + +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#include +#include + + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +template +struct basic_process; + +namespace posix +{ + + +namespace detail +{ + +struct base {}; +struct derived : base {}; + +template +inline error_code invoke_on_setup(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init && init, base && ) +{ + return error_code{}; +} + +template +inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init && init, derived && ) + -> decltype(init.on_setup(launcher, executable, cmd_line)) +{ + return init.on_setup(launcher, executable, cmd_line); +} + +template +inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line)) +{ + return error_code{}; +} + +template +inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init1 && init1, Inits && ... inits) +{ + auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{}); + if (ec) + return ec; + else + return on_setup(launcher, executable, cmd_line, inits...); +} + + +template +inline void invoke_on_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, Init && init, base && ) +{ +} + +template +inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, Init && init, derived && ) +-> decltype(init.on_error(launcher, ec, executable, cmd_line, ec)) +{ + init.on_error(launcher, executable, cmd_line, ec); +} + +template +inline void on_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec) +{ +} + +template +inline void on_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, + Init1 && init1, Inits && ... inits) +{ + invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{}); + on_error(launcher, executable, cmd_line, ec, inits...); +} + +template +inline void invoke_on_success(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init && init, base && ) +{ +} + +template +inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init && init, derived && ) +-> decltype(init.on_success(launcher, executable, cmd_line)) +{ + init.on_success(launcher, executable, cmd_line); +} + +template +inline void on_success(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line)) +{ +} + +template +inline void on_success(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init1 && init1, Inits && ... inits) +{ + invoke_on_success(launcher, executable, cmd_line, init1, derived{}); + on_success(launcher, executable, cmd_line, inits...); +} + +template +inline void invoke_on_fork_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, Init && init, base && ) +{ +} + +template +inline auto invoke_on_fork_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, Init && init, derived && ) +-> decltype(init.on_fork_error(launcher, ec, executable, cmd_line, ec)) +{ + init.on_fork_error(launcher, executable, cmd_line, ec); +} + +template +inline void on_fork_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec) +{ +} + +template +inline void on_fork_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, + Init1 && init1, Inits && ... inits) +{ + invoke_on_fork_error(launcher, executable, cmd_line, ec, init1, derived{}); + on_fork_error(launcher, executable, cmd_line, ec, inits...); +} + + + +template +inline void invoke_on_fork_success(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init && init, base && ) +{ + +} + +template +inline auto invoke_on_fork_success(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init && init, derived && ) +-> decltype(init.on_fork_success(launcher, executable, cmd_line)) +{ + init.on_fork_success(launcher, executable, cmd_line); +} + +template +inline void on_fork_success(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line)) +{ +} + +template +inline void on_fork_success(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init1 && init1, Inits && ... inits) +{ + invoke_on_fork_success(launcher, executable, cmd_line, init1, derived{}); + on_fork_success(launcher, executable, cmd_line, inits...); +} + + +template +inline error_code invoke_on_exec_setup(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init && init, base && ) +{ + return error_code{}; +} + +template +inline auto invoke_on_exec_setup(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init && init, derived && ) +-> decltype(init.on_exec_setup(launcher, executable, cmd_line)) +{ + return init.on_exec_setup(launcher, executable, cmd_line); +} + +template +inline error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line)) +{ + return error_code{}; +} + +template +inline error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + Init1 && init1, Inits && ... inits) +{ + auto ec = invoke_on_exec_setup(launcher, executable, cmd_line, init1, derived{}); + if (ec) + return ec; + else + return on_exec_setup(launcher, executable, cmd_line, inits...); +} + + + +template +inline void invoke_on_exec_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, Init && init, base && ) +{ +} + +template +inline auto invoke_on_exec_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, Init && init, derived && ) +-> decltype(init.on_exec_error(launcher, ec, executable, cmd_line, ec)) +{ + init.on_exec_error(launcher, executable, cmd_line, ec); +} + +template +inline void on_exec_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec) +{ +} + +template +inline void on_exec_error(Launcher & launcher, const filesystem::path &executable, + const char * const * (&cmd_line), + const error_code & ec, + Init1 && init1, Inits && ... inits) +{ + invoke_on_exec_error(launcher, executable, cmd_line, ec, init1, derived{}); + on_exec_error(launcher, executable, cmd_line, ec, inits...); +} +} + +/// The default launcher for processes on windows. +struct default_launcher +{ + const char * const * env = ::environ; + int pid; + + default_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) + asio::detail::throw_error(ec, "default_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(), executable, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::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) + asio::detail::throw_error(ec, "default_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto argv = this->build_argv_(executable, std::forward(args)); + { + pipe_guard pg; + if (::pipe(pg.p)) + { + ec.assign(errno, system_category()); + return basic_process{exec}; + } + if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC)) + { + ec.assign(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); + } + + auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query( + exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context); + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare); + pid = ::fork(); + if (pid == -1) + { + detail::on_fork_error(*this, executable, argv, ec, inits...); + detail::on_error(*this, executable, argv, ec, inits...); + + ec.assign(errno, system_category()); + return basic_process{exec}; + } + else if (pid == 0) + { + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); + ::close(pg.p[0]); + + ec = detail::on_exec_setup(*this, executable, argv, inits...); + if (!ec) + ::execve(executable.c_str(), const_cast(argv), const_cast(env)); + + ::write(pg.p[1], &errno, sizeof(int)); + ec.assign(errno, system_category()); + detail::on_exec_error(*this, executable, argv, ec, inits...); + ::exit(EXIT_FAILURE); + return basic_process{exec}; + } + + ::close(pg.p[1]); + pg.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)) + { + ec.assign(err, system_category()); + break; + } + } + if (count != 0) + ec.assign(child_error, system_category()); + + if (ec) + { + detail::on_error(*this, executable, argv, ec, inits...); + return basic_process{exec}; + } + } + basic_process proc{exec, pid}; + detail::on_success(*this, executable, argv, ec, inits...); + return proc; + + } + protected: + + struct pipe_guard + { + int p[2]; + pipe_guard() : p{-1,-1} {} + + ~pipe_guard() + { + if (p[0] != -1) + ::close(p[0]); + if (p[1] != -1) + ::close(p[1]); + } + }; + + //if we need to allocate something + std::vector argv_buffer_; + std::vector argv_; + + template + const char * const * build_argv_(const filesystem::path & pt, const Args & args, + typename std::enable_if< + std::is_convertible< + decltype(*std::begin(std::declval())), + cstring_ref>::value>::type * = nullptr) + { + const auto arg_cnt = std::distance(std::begin(args), std::end(args)); + argv_.reserve(arg_cnt + 2); + argv_.push_back(pt.native().data()); + for (auto && arg : args) + argv_.push_back(arg.c_str()); + + argv_.push_back(nullptr); + return argv_.data(); + } + + template + const char * const * build_argv_(const filesystem::path & pt, const Args & args, + typename std::enable_if< + !std::is_convertible< + decltype(*std::begin(std::declval())), + cstring_ref>::value>::type * = nullptr) + { + const auto arg_cnt = std::distance(std::begin(args), std::end(args)); + argv_.reserve(arg_cnt + 2); + argv_buffer_.reserve(arg_cnt); + argv_.push_back(pt.native().data()); + + using char_type = typename decay()))[0])>::type; + + for (basic_string_view arg : args) + argv_buffer_.push_back(v2::detail::conv_string(arg.data(), arg.size())); + + for (auto && arg : argv_buffer_) + argv_.push_back(arg.c_str()); + + argv_.push_back(nullptr); + return argv_.data(); + } +}; + + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER diff --git a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp new file mode 100644 index 000000000..4c2a2a56b --- /dev/null +++ b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp @@ -0,0 +1,132 @@ +// 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_FORK_AND_FORGET_LAUNCHER_HPP +#define BOOST_PROCESS_V2_POSIX_FORK_AND_FORGET_LAUNCHER_HPP + +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace posix +{ + +/// The default launcher for processes on windows. +struct fork_and_forget_launcher : default_launcher +{ + fork_and_forget_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) + asio::detail::throw_error(ec, "fork_and_forget_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(), executable, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::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) + asio::detail::throw_error(ec, "fork_and_forget_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto argv = this->build_argv_(executable, std::forward(args)); + { + ec = detail::on_setup(*this, executable, argv, inits ...); + if (ec) + { + detail::on_error(*this, executable, argv, ec, inits...); + return basic_process(exec); + } + + auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query( + exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context); + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare); + pid = ::fork(); + if (pid == -1) + { + detail::on_fork_error(*this, executable, argv, ec, inits...); + detail::on_error(*this, executable, argv, ec, inits...); + + ec.assign(errno, system_category()); + return basic_process{exec}; + } + else if (pid == 0) + { + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); + + ec = detail::on_exec_setup(*this, executable, argv, inits...); + if (!ec) + ::execve(executable.c_str(), const_cast(argv), const_cast(env)); + + ec.assign(errno, system_category()); + detail::on_exec_error(*this, executable, argv, ec, inits...); + ::exit(EXIT_FAILURE); + return basic_process{exec}; + } + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent); + if (ec) + { + detail::on_error(*this, executable, argv, ec, inits...); + return basic_process{exec}; + } + } + basic_process proc{exec, pid}; + detail::on_success(*this, executable, argv, ec, inits...); + return proc; + + } +}; + + +} + +BOOST_PROCESS_V2_END_NAMESPACE + + +#endif //BOOST_PROCESS_V2_POSIX_FORK_AND_FORGET_LAUNCHER_HPP diff --git a/include/boost/process/v2/posix/pdfork_launcher.hpp b/include/boost/process/v2/posix/pdfork_launcher.hpp new file mode 100644 index 000000000..10c3b7687 --- /dev/null +++ b/include/boost/process/v2/posix/pdfork_launcher.hpp @@ -0,0 +1,164 @@ +// 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_PDFORK_LAUNCHER_HPP +#define BOOST_PROCESS_V2_POSIX_PDFORK_LAUNCHER_HPP + +#include + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace posix +{ + +/// The default launcher for processes on windows. +struct pdfork_launcher : default_launcher +{ + int fd; + pdfork_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) + asio::detail::throw_error(ec, "pdfork_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(), executable, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::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) + asio::detail::throw_error(ec, "pdfork_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto argv = this->build_argv_(executable, std::forward(args)); + { + pipe_guard pg; + if (::pipe(pg.p)) + { + ec.assign(errno, system_category()); + return basic_process{exec}; + } + if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC)) + { + ec.assign(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); + } + + auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query( + exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context); + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare); + pid = ::pdfork(&fd, PD_DAEMON | PD_CLOEXEC); + if (pid == -1) + { + detail::on_fork_error(*this, executable, argv, ec, inits...); + detail::on_error(*this, executable, argv, ec, inits...); + + ec.assign(errno, system_category()); + return basic_process{exec}; + } + else if (pid == 0) + { + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); + ::close(pg.p[0]); + + ec = detail::on_exec_setup(*this, executable, argv, inits...); + if (!ec) + ::execve(executable.c_str(), const_cast(argv), const_cast(env)); + + ::write(pg.p[1], &errno, sizeof(int)); + ec.assign(errno, system_category()); + detail::on_exec_error(*this, executable, argv, ec, inits...); + ::exit(EXIT_FAILURE); + return basic_process{exec}; + } + + ::close(pg.p[1]); + pg.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)) + { + ec.assign(err, system_category()); + break; + } + } + if (count != 0) + ec.assign(child_error, system_category()); + + if (ec) + { + detail::on_error(*this, executable, argv, ec, inits...); + return basic_process{exec}; + } + } + 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_PDFORK_LAUNCHER_HPP diff --git a/include/boost/process/v2/posix/vfork_launcher.hpp b/include/boost/process/v2/posix/vfork_launcher.hpp new file mode 100644 index 000000000..b00500fa9 --- /dev/null +++ b/include/boost/process/v2/posix/vfork_launcher.hpp @@ -0,0 +1,133 @@ +// 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_VFORK_LAUNCHER_HPP +#define BOOST_PROCESS_V2_POSIX_VFORK_LAUNCHER_HPP + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace posix +{ + + +/// The default launcher for processes on windows. +struct vfork_launcher : default_launcher +{ + vfork_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) + asio::detail::throw_error(ec, "default_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(), executable, std::forward(args), std::forward(inits)...); + } + + template + auto operator()(Executor exec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::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) + asio::detail::throw_error(ec, "default_launcher"); + + return proc; + } + + template + auto operator()(Executor exec, + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process + { + auto argv = this->build_argv_(executable, std::forward(args)); + + ec = detail::on_setup(*this, executable, argv, inits ...); + if (ec) + { + detail::on_error(*this, executable, argv, ec, inits...); + return basic_process(exec); + } + + auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query( + exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context); + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare); + pid = ::vfork(); + if (pid == -1) + { + detail::on_fork_error(*this, executable, argv, ec, inits...); + detail::on_error(*this, executable, argv, ec, inits...); + + ec.assign(errno, system_category()); + return basic_process{exec}; + } + else if (pid == 0) + { + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); + ec = detail::on_exec_setup(*this, executable, argv, inits...); + if (!ec) + ::execve(executable.c_str(), const_cast(argv), const_cast(env)); + + ec.assign(errno, system_category()); + detail::on_exec_error(*this, executable, argv, ec, inits...); + ::exit(EXIT_FAILURE); + return basic_process{exec}; + } + ctx.notify_fork(asio::execution_context::fork_parent); + + if (ec) + { + detail::on_error(*this, executable, argv, ec, inits...); + return basic_process{exec}; + } + + basic_process proc{exec, pid}; + detail::on_success(*this, executable, argv, ec, inits...); + return proc; + + } +}; + + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_POSIX_VFORK_LAUNCHER_HPP diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index d36cb7b41..4a84ab32a 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -51,14 +51,16 @@ struct basic_process basic_process& operator=(const basic_process&) = delete; basic_process(basic_process&& lhs) - : attached_(lhs.attached_), + : process_handle_(std::move(lhs.process_handle_)), + attached_(lhs.attached_), terminated_(lhs.terminated_), - exit_status_{lhs.exit_status_}, - process_handle_(std::move(lhs.process_handle_)) + exit_status_{lhs.exit_status_} + { lhs.attached_ = false; } + basic_process& operator=(basic_process&& lhs) { attached_ = lhs.attached_; @@ -68,6 +70,17 @@ struct basic_process lhs.attached_ = false; return *this; } + template + basic_process(basic_process&& lhs) + : process_handle_(std::move(lhs.process_handle_)), + attached_(lhs.attached_), + terminated_(lhs.terminated_), + exit_status_{lhs.exit_status_} + + + { + lhs.attached_ = false; + } /// Construct a child from a property list and launch it. template @@ -111,7 +124,8 @@ struct basic_process const filesystem::path&>::type exe, std::initializer_list args, Inits&&... inits) - : basic_process(default_process_launcher()(executor_type(context.get_executor()), exe, args, std::forward(inits)...)) + : basic_process(default_process_launcher()(executor_type(context.get_executor()), + exe, args, std::forward(inits)...)) { } /// Construct a child from a property list and launch it. @@ -266,7 +280,7 @@ struct basic_process } bool is_open() const { return process_handle_.is_open(); } - explicit operator bool() const {return valid(); } + explicit operator bool() const {return is_open(); } template + friend struct basic_process; + basic_process_handle process_handle_; bool attached_{true}; bool terminated_{false}; diff --git a/include/boost/process/v2/process_handle.hpp b/include/boost/process/v2/process_handle.hpp index 926fe23e2..6080a4db7 100644 --- a/include/boost/process/v2/process_handle.hpp +++ b/include/boost/process/v2/process_handle.hpp @@ -11,8 +11,42 @@ #include #else +#if defined(BOOST_PROCESS_V2_PIDFD_OPEN) +#include +#elif defined(BOOST_PROCESS_V2_PDFORK) +#include +#else +// with asio support we could use EVFILT_PROC:NOTE_EXIT as well. +#include +#endif #endif +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(BOOST_PROCESS_V2_WINDOWS) +template +using basic_process_handle = detail::basic_process_handle_win; +#else + +#if defined(BOOST_PROCESS_V2_PIDFD_OPEN) +template +using basic_process_handle = detail::basic_process_handle_fd; +#elif defined(BOOST_PROCESS_V2_PDFORK) +template +using basic_process_handle = detail::basic_process_handle_fd_or_signal; +#else + +template +using basic_process_handle = detail::basic_process_handle_signal; + +#endif + +using process_handle = basic_process_handle<>; + +#endif + +BOOST_PROCESS_V2_END_NAMESPACE + #if defined(BOOST_PROCESS_V2_HEADER_ONLY) #include diff --git a/include/boost/process/v2/start_dir.hpp b/include/boost/process/v2/start_dir.hpp index 32bcb05d0..950089bd1 100644 --- a/include/boost/process/v2/start_dir.hpp +++ b/include/boost/process/v2/start_dir.hpp @@ -35,7 +35,7 @@ struct process_start_dir const filesystem::path &, const char * const *) { if (::chdir(start_dir.c_str()) == -1) - return get_last_error(); + return detail::get_last_error(); else return error_code (); } diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index 0522b1114..937c78566 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -15,6 +15,19 @@ boost_process_v2_standalone_test(utf8) boost_process_v2_standalone_test(cstring_ref) boost_process_v2_standalone_test(pid) boost_process_v2_standalone_test(environment) -boost_process_v2_standalone_test(exit_code) -add_library(boost_process_v2_header_test header_1.cpp header_2.cpp) \ No newline at end of file +add_library(boost_process_v2_header_test header_1.cpp header_2.cpp) + +add_executable(boost_process_v2_test_target target.cpp) +target_link_libraries(boost_process_v2_test_target PUBLIC Boost::system) +target_include_directories(boost_process_v2_test_target PUBLIC ../../../..) + +function(boost_process_v2_test_with_target name) + add_executable(boost_process_v2_${name} ${name}.cpp) + target_link_libraries(boost_process_v2_${name} Boost::system Boost::filesystem boost_process_v2_test_impl) + add_dependencies(boost_process_v2_${name} boost_process_v2_test_target) + add_test(NAME boost_process_v2_${name} COMMAND $ + -- $) +endfunction() + +boost_process_v2_test_with_target(process) \ No newline at end of file diff --git a/test/v2/process.cpp b/test/v2/process.cpp index c6ab59ec8..32f91a745 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -28,6 +28,7 @@ #include #include +#include namespace bpv = boost::process::v2; namespace asio = boost::asio; @@ -173,7 +174,7 @@ BOOST_AUTO_TEST_CASE(print_args_out) auto sz = asio::read(rp, st, ec); - BOOST_CHECK_NE(sz, 0); + BOOST_CHECK_NE(sz, 0u); BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); std::string line; @@ -361,7 +362,6 @@ std::string read_env(const char * name, Inits && ... inits) auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); - printf("FOOBAR %lld '%s'\n", sz, out.c_str()); trim_end(out); proc.wait(); diff --git a/test/v2/target.cpp b/test/v2/target.cpp index ea24a85aa..80efae067 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -4,8 +4,11 @@ #if defined(BOOST_PROCESS_V2_WINDOWS) #include +#else +#include #endif + int main(int argc, char * argv[]) { std::string mode = argv[1]; @@ -29,7 +32,8 @@ int main(int argc, char * argv[]) const auto sz = ::GetCurrentDirectoryW(sizeof(buf), buf); std::wcout << boost::process::v2::wstring_view(buf, sz) << std::flush; #else - + char buf[65535]; + printf(::getcwd(buf, sizeof(buf))); #endif } else if (mode == "check-eof") From 00bc1ccf472a48c6fe8e1ab63946fd532c31889d Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Mon, 30 May 2022 11:47:58 +0800 Subject: [PATCH 059/397] Fixed windows extra launchers. --- .../v2/detail/process_handle_windows.hpp | 16 +++++++- .../process/v2/windows/as_user_launcher.hpp | 40 +++++++++---------- .../process/v2/windows/default_launcher.hpp | 5 ++- .../v2/windows/with_logon_launcher.hpp | 12 +++--- .../v2/windows/with_token_launcher.hpp | 12 +++--- test/v2/Jamfile.jam | 4 +- test/v2/process.cpp | 7 +++- test/v2/target.cpp | 14 +++++++ 8 files changed, 71 insertions(+), 39 deletions(-) diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp index b4e4d8ff9..d18eb03eb 100644 --- a/include/boost/process/v2/detail/process_handle_windows.hpp +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -9,6 +9,7 @@ #include #include +#include #if defined(BOOST_PROCESS_V2_STANDALONE) #include @@ -79,11 +80,24 @@ struct basic_process_handle_win { } +/* template basic_process_handle_win(basic_process_handle_win && handle) - : pid_(handle.pid_), handle_(std::move(handle).handle_) + : pid_(handle.pid_), handle_(handle.handle_.get_executor()) { } +*/ + basic_process_handle_win(basic_process_handle_win && ) = default; + basic_process_handle_win& operator=(basic_process_handle_win && ) = default; + + ~basic_process_handle_win() + { + if (handle_.is_open()) + { + error_code ec; + handle_.close(ec); + } + } native_handle_type native_handle() { return handle_.native_handle(); } diff --git a/include/boost/process/v2/windows/as_user_launcher.hpp b/include/boost/process/v2/windows/as_user_launcher.hpp index 7157d0b08..604852eca 100644 --- a/include/boost/process/v2/windows/as_user_launcher.hpp +++ b/include/boost/process/v2/windows/as_user_launcher.hpp @@ -11,6 +11,7 @@ #ifndef BOOST_PROCESS_V2_WINDOWS_AS_USER_LAUNCHER_HPP #define BOOST_PROCESS_V2_WINDOWS_AS_USER_LAUNCHER_HPP +#include #include BOOST_PROCESS_V2_BEGIN_NAMESPACE @@ -23,10 +24,8 @@ struct as_user_launcher : default_launcher HANDLE token; as_user_launcher(HANDLE token = INVALID_HANDLE_VALUE) : token(token) {} - template auto operator()(ExecutionContext & context, - error_code & ec, const typename std::enable_if::value, filesystem::path >::type & executable, @@ -34,7 +33,7 @@ struct as_user_launcher : default_launcher Inits && ... inits ) -> basic_process { error_code ec; - auto proc = (*this)(context, ec, path, std::forward(args), std::forward(inits)...); + auto proc = (*this)(context, ec, executable, std::forward(args), std::forward(inits)...); if (ec) asio::detail::throw_error(ec, "as_user_launcher"); @@ -52,12 +51,11 @@ struct as_user_launcher : default_launcher Args && args, Inits && ... inits ) -> basic_process { - return (*this)(context.get_executor(), path, std::forward(args), std::forward(inits)...); + return (*this)(context.get_executor(), executable, std::forward(args), std::forward(inits)...); } template auto operator()(Executor exec, - error_code & ec, const typename std::enable_if< BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, @@ -66,7 +64,7 @@ struct as_user_launcher : default_launcher Inits && ... inits ) -> basic_process { error_code ec; - auto proc = (*this)(std::move(exec), ec, path, std::forward(args), std::forward(inits)...); + auto proc = (*this)(std::move(exec), ec, executable, std::forward(args), std::forward(inits)...); if (ec) asio::detail::throw_error(ec, "as_user_launcher"); @@ -76,17 +74,17 @@ struct as_user_launcher : default_launcher template auto operator()(Executor exec, - error_code & ec, - const typename std::enable_if< - BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || - BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, - filesystem::path >::type & executable, - Args && args, - Inits && ... inits ) -> basic_process + error_code & ec, + const typename std::enable_if< + BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || + BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, + filesystem::path >::type & executable, + Args && args, + Inits && ... inits ) -> basic_process { - auto command_line = this->build_command_line_(executable, args); + auto command_line = this->build_command_line(executable, args); - ec = on_init_(*this, executable, command_line, inits...); + ec = detail::on_setup(*this, executable, command_line, inits...); if (ec) { detail::on_error(*this, executable, command_line, ec, inits...); @@ -95,25 +93,25 @@ struct as_user_launcher : default_launcher auto ok = ::CreateProcessAsUserW( token, executable.empty() ? nullptr : executable.c_str(), - command_line.empty() ? nullptr : command_line.c_str(), + command_line.empty() ? nullptr : &command_line.front(), process_attributes, thread_attributes, inherit_handles ? TRUE : FALSE, creation_flags, environment, current_directory.empty() ? nullptr : current_directory.c_str(), - &startup_info, + &startup_info.StartupInfo, &process_information); if (ok == 0) { - ec.assign(::GetLastError(), error::get_system_category()); + ec = detail::get_last_error(); detail::on_error(*this, executable, command_line, ec, inits...); - if (process_information.hProcess != INVALID_HANDLE) + if (process_information.hProcess != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hProcess); - if (process_information.hThread != INVALID_HANDLE) + if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec); @@ -121,7 +119,7 @@ struct as_user_launcher : default_launcher { detail::on_success(*this, executable, command_line, inits...); - if (process_information.hThread != INVALID_HANDLE) + if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec, diff --git a/include/boost/process/v2/windows/default_launcher.hpp b/include/boost/process/v2/windows/default_launcher.hpp index 2e2f22593..095af078f 100644 --- a/include/boost/process/v2/windows/default_launcher.hpp +++ b/include/boost/process/v2/windows/default_launcher.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -318,10 +319,10 @@ struct default_launcher else { detail::on_success(*this, executable, command_line, inits...); -/* + if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); -*/ + return basic_process(exec, this->process_information.dwProcessId, this->process_information.hProcess); diff --git a/include/boost/process/v2/windows/with_logon_launcher.hpp b/include/boost/process/v2/windows/with_logon_launcher.hpp index 6c2dafeb7..fe8b080d9 100644 --- a/include/boost/process/v2/windows/with_logon_launcher.hpp +++ b/include/boost/process/v2/windows/with_logon_launcher.hpp @@ -95,9 +95,9 @@ struct with_logon_launcher : default_launcher Args && args, Inits && ... inits ) -> basic_process { - auto command_line = this->build_command_line_(executable, args); + auto command_line = this->build_command_line(executable, args); - ec = on_init_(*this, executable, command_line, inits...); + ec = detail::on_setup(*this, executable, command_line, inits...); if (ec) { detail::on_error(*this, executable, command_line, ec, inits...); @@ -109,7 +109,7 @@ struct with_logon_launcher : default_launcher password.c_str(), logon_flags executable.empty() ? nullptr : executable.c_str(), - command_line.empty() ? nullptr : command_line.c_str(), + command_line.empty() ? nullptr : &command_line.front(), process_attributes, thread_attributes, inherit_handles ? TRUE : FALSE, @@ -125,9 +125,9 @@ struct with_logon_launcher : default_launcher ec.assign(::GetLastError(), error::get_system_category()); detail::on_error(*this, executable, command_line, ec, inits...); - if (process_information.hProcess != INVALID_HANDLE) + if (process_information.hProcess != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hProcess); - if (process_information.hThread != INVALID_HANDLE) + if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec); @@ -135,7 +135,7 @@ struct with_logon_launcher : default_launcher { detail::on_success(*this, executable, command_line, inits...); - if (process_information.hThread != INVALID_HANDLE) + if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec, diff --git a/include/boost/process/v2/windows/with_token_launcher.hpp b/include/boost/process/v2/windows/with_token_launcher.hpp index a03bdc349..dfca7f84e 100644 --- a/include/boost/process/v2/windows/with_token_launcher.hpp +++ b/include/boost/process/v2/windows/with_token_launcher.hpp @@ -86,9 +86,9 @@ struct with_token_launcher : default_launcher Args && args, Inits && ... inits ) -> basic_process { - auto command_line = this->build_command_line_(executable, args); + auto command_line = this->build_command_line(executable, args); - ec = on_init_(*this, executable, command_line, inits...); + ec = detail::on_setup(*this, executable, command_line, inits...); if (ec) { detail::on_error(*this, executable, command_line, ec, inits...); @@ -98,7 +98,7 @@ struct with_token_launcher : default_launcher token, logon_flags executable.empty() ? nullptr : executable.c_str(), - command_line.empty() ? nullptr : command_line.c_str(), + command_line.empty() ? nullptr : &command_line.front(), process_attributes, thread_attributes, inherit_handles ? TRUE : FALSE, @@ -114,9 +114,9 @@ struct with_token_launcher : default_launcher ec.assign(::GetLastError(), error::get_system_category()); detail::on_error(*this, executable, command_line, ec, inits...); - if (process_information.hProcess != INVALID_HANDLE) + if (process_information.hProcess != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hProcess); - if (process_information.hThread != INVALID_HANDLE) + if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec); @@ -124,7 +124,7 @@ struct with_token_launcher : default_launcher { detail::on_success(*this, executable, command_line, inits...); - if (process_information.hThread != INVALID_HANDLE) + if (process_information.hThread != INVALID_HANDLE_VALUE) ::CloseHandle(process_information.hThread); return basic_process(exec, diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index b0bffda4a..b2371adaa 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -32,7 +32,8 @@ import testing ; alias filesystem : /boost//filesystem ; exe target : target.cpp filesystem : - off windows:shell32 windows:Ntdll + off windows:shell32 + windows:Ntdll ; @@ -55,5 +56,6 @@ test-suite standalone : test-suite with_target : [ run process.cpp test_impl : : target ] + [ run windows.cpp test_impl : : target : no windows:yes windows:Advapi32 ] ; diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 32f91a745..a390e8ab3 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -75,8 +75,11 @@ BOOST_AUTO_TEST_CASE(exit_code_sync) BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "0"}).wait(), 0); BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "1"}).wait(), 1); - BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "2"}).wait(), 2); - BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "42"}).wait(), 42); + std::vector args = {"exit-code", "2"}; + BOOST_CHECK_EQUAL(bpv::default_process_launcher()(ctx, pth, args).wait(), 2); + args[1] = "42"; + auto proc = bpv::default_process_launcher()(ctx, pth, args); + BOOST_CHECK_EQUAL(proc.wait(), 42); } diff --git a/test/v2/target.cpp b/test/v2/target.cpp index 80efae067..4f0903e77 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -47,6 +47,20 @@ int main(int argc, char * argv[]) auto p = ::getenv(argv[2]); assert(printf("%s", p) > 0); } +#if defined(BOOST_PROCESS_V2_WINDOWS) + else if (mode == "showwindow") + { + STARTUPINFO si; + GetStartupInfo(&si); + return static_cast(si.wShowWindow); + } + else if (mode == "creation-flags") + { + STARTUPINFO si; + GetStartupInfo(&si); + return static_cast(si.dwFlags); + } +#endif else return 34; From 932ac3038e857ca105da5ff1a1532fca218517e2 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 31 May 2022 10:28:00 +0800 Subject: [PATCH 060/397] Added tests for windows extra launchers. --- .../v2/detail/process_handle_windows.hpp | 4 +- .../process/v2/windows/default_launcher.hpp | 2 +- .../v2/windows/with_logon_launcher.hpp | 23 +--- .../v2/windows/with_token_launcher.hpp | 19 ++- test/v2/windows.cpp | 113 ++++++++++++++++++ 5 files changed, 129 insertions(+), 32 deletions(-) create mode 100644 test/v2/windows.cpp diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp index d18eb03eb..64c127a1c 100644 --- a/include/boost/process/v2/detail/process_handle_windows.hpp +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -80,13 +80,13 @@ struct basic_process_handle_win { } -/* + template basic_process_handle_win(basic_process_handle_win && handle) : pid_(handle.pid_), handle_(handle.handle_.get_executor()) { } -*/ + basic_process_handle_win(basic_process_handle_win && ) = default; basic_process_handle_win& operator=(basic_process_handle_win && ) = default; diff --git a/include/boost/process/v2/windows/default_launcher.hpp b/include/boost/process/v2/windows/default_launcher.hpp index 095af078f..dd78881a9 100644 --- a/include/boost/process/v2/windows/default_launcher.hpp +++ b/include/boost/process/v2/windows/default_launcher.hpp @@ -251,7 +251,7 @@ struct default_launcher Args && args, Inits && ... inits ) -> enable_init { - return (*this)(context.get_executor(), executable, std::forward(args), std::forward(inits)...); + return (*this)(context.get_executor(), ec, executable, std::forward(args), std::forward(inits)...); } template diff --git a/include/boost/process/v2/windows/with_logon_launcher.hpp b/include/boost/process/v2/windows/with_logon_launcher.hpp index fe8b080d9..08244e9c5 100644 --- a/include/boost/process/v2/windows/with_logon_launcher.hpp +++ b/include/boost/process/v2/windows/with_logon_launcher.hpp @@ -44,31 +44,23 @@ struct with_logon_launcher : default_launcher Args && args, Inits && ... inits ) -> basic_process { - error_code ec; - auto proc = (*this)(context, ec, path, std::forward(args), std::forward(inits)...); - - if (ec) - asio::detail::throw_error(ec, "with_logon_launcher"); - - return proc; + return (*this)(context.get_executor(), ec, executable, std::forward(args), std::forward(inits)...); } 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(), path, std::forward(args), std::forward(inits)...); + return (*this)(context.get_executor(), executable, std::forward(args), std::forward(inits)...); } template auto operator()(Executor exec, - error_code & ec, const typename std::enable_if< BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, @@ -77,7 +69,7 @@ struct with_logon_launcher : default_launcher Inits && ... inits ) -> basic_process { error_code ec; - auto proc = (*this)(std::move(exec), ec, path, std::forward(args), std::forward(inits)...); + auto proc = (*this)(std::move(exec), ec, executable, std::forward(args), std::forward(inits)...); if (ec) asio::detail::throw_error(ec, "with_logon_launcher"); @@ -107,22 +99,19 @@ struct with_logon_launcher : default_launcher username.c_str(), domain.empty() ? nullptr : domain.c_str(), password.c_str(), - logon_flags + logon_flags, executable.empty() ? nullptr : executable.c_str(), command_line.empty() ? nullptr : &command_line.front(), - process_attributes, - thread_attributes, - inherit_handles ? TRUE : FALSE, creation_flags, environment, current_directory.empty() ? nullptr : current_directory.c_str(), - &startup_info, + &startup_info.StartupInfo, &process_information); if (ok == 0) { - ec.assign(::GetLastError(), error::get_system_category()); + ec = detail::get_last_error(); detail::on_error(*this, executable, command_line, ec, inits...); if (process_information.hProcess != INVALID_HANDLE_VALUE) diff --git a/include/boost/process/v2/windows/with_token_launcher.hpp b/include/boost/process/v2/windows/with_token_launcher.hpp index dfca7f84e..040eefaf5 100644 --- a/include/boost/process/v2/windows/with_token_launcher.hpp +++ b/include/boost/process/v2/windows/with_token_launcher.hpp @@ -28,7 +28,6 @@ struct with_token_launcher : default_launcher template auto operator()(ExecutionContext & context, - error_code & ec, const typename std::enable_if::value, filesystem::path >::type & executable, @@ -36,7 +35,7 @@ struct with_token_launcher : default_launcher Inits && ... inits ) -> basic_process { error_code ec; - auto proc = (*this)(context, ec, path, std::forward(args), std::forward(inits)...); + auto proc = (*this)(context, ec, executable, std::forward(args), std::forward(inits)...); if (ec) asio::detail::throw_error(ec, "with_token_launcher"); @@ -54,12 +53,11 @@ struct with_token_launcher : default_launcher Args && args, Inits && ... inits ) -> basic_process { - return (*this)(context.get_executor(), path, std::forward(args), std::forward(inits)...); + return (*this)(context.get_executor(), ec, executable, std::forward(args), std::forward(inits)...); } template auto operator()(Executor exec, - error_code & ec, const typename std::enable_if< BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor::value || BOOST_PROCESS_V2_ASIO_NAMESPACE::is_executor::value, @@ -68,7 +66,7 @@ struct with_token_launcher : default_launcher Inits && ... inits ) -> basic_process { error_code ec; - auto proc = (*this)(std::move(exec), ec, path, std::forward(args), std::forward(inits)...); + auto proc = (*this)(std::move(exec), ec, executable, std::forward(args), std::forward(inits)...); if (ec) asio::detail::throw_error(ec, "with_token_launcher"); @@ -94,24 +92,21 @@ struct with_token_launcher : default_launcher detail::on_error(*this, executable, command_line, ec, inits...); return basic_process(exec); } - auto ok = ::CreateProcessWithTokenW ( + auto ok = ::CreateProcessWithTokenW( token, - logon_flags + logon_flags, executable.empty() ? nullptr : executable.c_str(), command_line.empty() ? nullptr : &command_line.front(), - process_attributes, - thread_attributes, - inherit_handles ? TRUE : FALSE, creation_flags, environment, current_directory.empty() ? nullptr : current_directory.c_str(), - &startup_info, + &startup_info.StartupInfo, &process_information); if (ok == 0) { - ec.assign(::GetLastError(), error::get_system_category()); + ec = detail::get_last_error(); detail::on_error(*this, executable, command_line, ec, inits...); if (process_information.hProcess != INVALID_HANDLE_VALUE) diff --git a/test/v2/windows.cpp b/test/v2/windows.cpp new file mode 100644 index 000000000..9b6d3c61d --- /dev/null +++ b/test/v2/windows.cpp @@ -0,0 +1,113 @@ +// 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) + + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +#if defined(BOOST_FILESYSTEM_DYN_LINK) +#undef BOOST_FILESYSTEM_DYN_LINK +#endif + +// Test that header file is self-contained. +#include + + +#include +#include +#include +#include +#include + + +#include +#include + +#include +#include + +namespace bpv = boost::process::v2; +namespace asio = boost::asio; + +BOOST_AUTO_TEST_SUITE(windows); + + +BOOST_AUTO_TEST_CASE(show_window) +{ + using boost::unit_test::framework::master_test_suite; + asio::io_context ctx; + const auto pth = master_test_suite().argv[1]; + bpv::process proc{ctx, pth, {"showwindow"}}; + + BOOST_CHECK_EQUAL(proc.wait(), 0); + + proc = bpv::process{ctx, pth, {"showwindow"}, bpv::windows::show_window_minimized_not_active}; + BOOST_CHECK_EQUAL(proc.wait(), SW_SHOWMINNOACTIVE); + +} + +BOOST_AUTO_TEST_CASE(creation_flags) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + asio::io_context ctx; + bpv::process proc{ctx, pth, {"creation-flags"}}; + + BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, 0); + + proc = bpv::process{ctx, master_test_suite().argv[1], {"creation-flags"}, bpv::windows::process_creation_flags()}; + BOOST_CHECK(proc); + BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, STARTF_TITLEISAPPID); +} + +BOOST_AUTO_TEST_CASE(as_user_launcher) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + std::vector args = {"exit-code", "2"}; + + HANDLE token_handle = INVALID_HANDLE_VALUE; + BOOST_CHECK(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, &token_handle) != 0); + bpv::error_code ec; + auto proc = bpv::windows::as_user_launcher(token_handle)(ctx, ec, pth, args); + BOOST_CHECK_EQUAL(proc.wait(), 2); +} + +BOOST_AUTO_TEST_CASE(with_logon_launcher) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + std::vector args = {"exit-code", "42"}; + + bpv::error_code ec; + auto proc = bpv::windows::with_logon_launcher(L"idiot", L"changeme")(ctx, ec, pth, args); + BOOST_CHECK_EQUAL(ec.value(), ERROR_INVALID_PARAMETER); +} + + +BOOST_AUTO_TEST_CASE(with_token_launcher) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + std::vector args = {"exit-code", "2"}; + + HANDLE token_handle = INVALID_HANDLE_VALUE; + BOOST_CHECK(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, &token_handle) != 0); + bpv::error_code ec; + auto proc = bpv::windows::with_token_launcher(nullptr)(ctx, ec, pth, args); + BOOST_CHECK_EQUAL(ec.value(), ERROR_INVALID_PARAMETER); +} + + +BOOST_AUTO_TEST_SUITE_END(); + From 5e5e0b8641026a93f5538f87638e2d80aa03f716 Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 31 May 2022 12:55:14 +0800 Subject: [PATCH 061/397] Minor doc additions. --- include/boost/process/v2/process.hpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index 4a84ab32a..f42bf3283 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -29,11 +29,22 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE template struct basic_process { + /// The executor of the process using executor_type = Executor; + /// Get the executor of the process executor_type get_executor() {return process_handle_.get_executor();} + /// The non-closing handle type + using handle_type = basic_process_handle; + + /// Get the underlying non-closing handle + handle_type & handle() { return process_handle_; } + + /// Get the underlying non-closing handle + const handle_type & handle() const { return process_handle_; } + /// Provides access to underlying operating system facilities - using native_handle_type = typename basic_process_handle::native_handle_type; + using native_handle_type = typename handle_type::native_handle_type; /// Rebinds the process_handle to another executor. template From 1493e365ed7aca3a76d04e3a632abe04f452a32f Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 31 May 2022 13:24:36 +0800 Subject: [PATCH 062/397] FreeBSD fixes. --- include/boost/process/v2/detail/environment_posix.hpp | 4 ++++ include/boost/process/v2/environment.hpp | 9 --------- include/boost/process/v2/posix/default_launcher.hpp | 3 +++ test/v2/environment.cpp | 4 ---- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/include/boost/process/v2/detail/environment_posix.hpp b/include/boost/process/v2/detail/environment_posix.hpp index 23eb74420..bcfc47607 100644 --- a/include/boost/process/v2/detail/environment_posix.hpp +++ b/include/boost/process/v2/detail/environment_posix.hpp @@ -14,6 +14,10 @@ #include #include +#if defined(__FreeBSD__) +extern "C" { extern char **environ; } +#endif + BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace environment diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index 619d697d2..1ce41d5e7 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -1062,15 +1062,6 @@ struct key_value_pair return is; } - template - auto get() const - { - if constexpr (Idx == 0u) - return key_view(); - else - return value_view(); - } - template inline auto get() const -> typename conditional #include +#if defined(__FreeBSD__) +extern "C" { extern char **environ; } +#endif BOOST_PROCESS_V2_BEGIN_NAMESPACE diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index 8ce134fd1..1e923783e 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -62,11 +62,7 @@ BOOST_AUTO_TEST_CASE(environment) ec.clear(); for (auto && ke : bpe::current()) - { - std::wcerr << "KV 1 " << ke << std::endl; - std::wcerr << "KV 2 " << ke.c_str() << std::endl; BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); - } #if defined(BOOST_PROCESS_V2_POSIX) From 1f4567751879ed317f0eb98094ff380600ef9ec3 Mon Sep 17 00:00:00 2001 From: Klemens Date: Wed, 1 Jun 2022 12:43:57 +0800 Subject: [PATCH 063/397] Added exit-code error category. --- include/boost/process/v2/error.hpp | 4 + include/boost/process/v2/impl/error.ipp | 156 +++++++++++++++++++++++- 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/include/boost/process/v2/error.hpp b/include/boost/process/v2/error.hpp index ca3c4da38..f186f0bbc 100644 --- a/include/boost/process/v2/error.hpp +++ b/include/boost/process/v2/error.hpp @@ -20,6 +20,10 @@ enum utf8_conv_error extern BOOST_PROCESS_V2_DECL const error_category& get_utf8_category(); static const error_category& utf8_category = get_utf8_category(); + +extern BOOST_PROCESS_V2_DECL const error_category& get_exit_code_category(); +static const error_category& exit_code_category = get_exit_code_category(); + } BOOST_PROCESS_V2_END_NAMESPACE diff --git a/include/boost/process/v2/impl/error.ipp b/include/boost/process/v2/impl/error.ipp index 4bd8ba791..1df8d92a5 100644 --- a/include/boost/process/v2/impl/error.ipp +++ b/include/boost/process/v2/impl/error.ipp @@ -7,7 +7,13 @@ #include #include +#include +#include + +#if defined(BOOST_PROCESS_V2_POSIX) +#include +#endif BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace error @@ -16,7 +22,6 @@ namespace error namespace detail { -// can be replaced with filesystem::codecvt_error_category in boost struct utf8_category final : public error_category { utf8_category() : error_category(0xDAEDu) {} @@ -40,6 +45,149 @@ struct utf8_category final : public error_category }; +struct exit_code_category final : public error_category +{ + exit_code_category() : error_category(0xDAEEu) {} + + const char* name() const noexcept + { + return "process.v2.exit_code"; + } + std::string message(int status) const + { + switch (status) + { + case v2::detail::still_active: + return "still-active"; + case EXIT_SUCCESS: + return "exit_success"; + case EXIT_FAILURE: + return "exit_failure"; + default: +#if defined(BOOST_PROCESS_V2_POSIX) + if (WIFCONTINUED(status)) + return "continued"; + switch (WTERMSIG(status)) + { +# if defined(SIGABRT) + case SIGABRT: return "SIGABRT: Abort signal from abort(3)"; +# endif +# if defined(SIGALRM) + case SIGALRM: return "SIGALRM: Timer signal from alarm(2)"; +# endif +# if defined(SIGBUS) + case SIGBUS: return "SIGBUS: Bus error (bad memory access)"; +# endif +# if defined(SIGCHLD) + case SIGCHLD: return "SIGCHLD: Child stopped or terminated"; +# endif +# if defined(SIGCONT) + case SIGCONT: return "SIGCONT: Continue if stopped"; +# endif +# if defined(SIGEMT) + case SIGEMT: return "SIGEMT: Emulator trap"; +# endif +# if defined(SIGFPE) + case SIGFPE: return "SIGFPE: Floating-point exception"; +# endif +# if defined(SIGHUP) + case SIGHUP: return "SIGHUP: Hangup detected on controlling terminal"; +# endif +# if defined(SIGILL) + case SIGILL: return "SIGILL: Illegal Instruction"; +# endif +# if defined(SIGINFO) + case SIGINFO: return "SIGINFO: A synonym for SIGPWR"; +# endif +# if defined(SIGINT) + case SIGINT: return "SIGINT: Interrupt from keyboard"; +# endif +# if defined(SIGIO) + case SIGIO: return "SIGIO: I/O now possible (4.2BSD)"; +# endif +# if defined(SIGKILL) + case SIGKILL: return "SIGKILL: Kill signal"; +# endif +# if defined(SIGLOST) + case SIGLOST: return "SIGLOST: File lock lost (unused)"; +# endif +# if defined(SIGPIPE) + case SIGPIPE: return "SIGPIPE: Broken pipe: write to pipe with no"; +# endif +# if defined(SIGPOLL) && !defined(SIGIO) + case SIGPOLL: return "SIGPOLL: Pollable event (Sys V);"; +# endif +# if defined(SIGPROF) + case SIGPROF: return "SIGPROF: Profiling timer expired"; +# endif +# if defined(SIGPWR) + case SIGPWR: return "SIGPWR: Power failure (System V)"; +# endif +# if defined(SIGQUIT) + case SIGQUIT: return "SIGQUIT: Quit from keyboard"; +# endif +# if defined(SIGSEGV) + case SIGSEGV: return "SIGSEGV: Invalid memory reference"; +# endif +# if defined(SIGSTKFLT) + case SIGSTKFLT: return "SIGSTKFLT: Stack fault on coprocessor (unused)"; +# endif +# if defined(SIGSTOP) + case SIGSTOP: return "SIGSTOP: Stop process"; +# endif +# if defined(SIGTSTP) + case SIGTSTP: return "SIGTSTP: Stop typed at terminal"; +# endif +# if defined(SIGSYS) + case SIGSYS: return "SIGSYS: Bad system call (SVr4);"; +# endif +# if defined(SIGTERM) + case SIGTERM: return "SIGTERM: Termination signal"; +# endif +# if defined(SIGTRAP) + case SIGTRAP: return "SIGTRAP: Trace/breakpoint trap"; +# endif +# if defined(SIGTTIN) + case SIGTTIN: return "SIGTTIN: Terminal input for background process"; +# endif +# if defined(SIGTTOU) + case SIGTTOU: return "SIGTTOU: Terminal output for background process"; +# endif +# if defined(SIGUNUSED) + case SIGUNUSED: return "SIGUNUSED: Synonymous with SIGSYS"; +# endif +# if defined(SIGURG) + case SIGURG: return "SIGURG: Urgent condition on socket (4.2BSD)"; +# endif +# if defined(SIGUSR1) + case SIGUSR1: return "SIGUSR1: User-defined signal 1"; +# endif +# if defined(SIGUSR2) + case SIGUSR2: return "SIGUSR2: User-defined signal 2"; +# endif +# if defined(SIGVTALRM) + case SIGVTALRM: return "SIGVTALRM: Virtual alarm clock (4.2BSD)"; +# endif +# if defined(SIGXCPU) + case SIGXCPU: return "SIGXCPU: CPU time limit exceeded (4.2BSD);"; +# endif +# if defined(SIGXFSZ) + case SIGXFSZ: return "SIGXFSZ: File size limit exceeded (4.2BSD);"; +# endif +# if defined(SIGWINCH) + case SIGWINCH: return "SIGWINCH: Window resize signal (4.3BSD, Sun)"; +# endif + default: "Unknown signal"; + } +#endif + return "exited with other error"; + } + } +}; + + + + } // namespace detail BOOST_PROCESS_V2_DECL const error_category& get_utf8_category() @@ -48,6 +196,12 @@ BOOST_PROCESS_V2_DECL const error_category& get_utf8_category() return instance; } +BOOST_PROCESS_V2_DECL const error_category& get_exit_code_category() +{ + static detail::exit_code_category instance; + return instance; +} + } BOOST_PROCESS_V2_END_NAMESPACE From 54b698dcbdfd79f00eeef8f8b61fec58dbb5d550 Mon Sep 17 00:00:00 2001 From: Klemens Date: Wed, 1 Jun 2022 13:48:51 +0800 Subject: [PATCH 064/397] Added special treatment for pipes in stdio. --- include/boost/process/v2/process.hpp | 4 +- include/boost/process/v2/stdio.hpp | 79 ++++++++++++++++++++++++++-- test/v2/process.cpp | 5 +- test/v2/target.cpp | 2 + 4 files changed, 81 insertions(+), 9 deletions(-) diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index f42bf3283..ff67e9649 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -274,7 +274,7 @@ struct basic_process error_code ec; native_exit_code_type exit_code; auto r = process_handle_.running(exit_code, ec); - if (!ec) + if (!ec && !r) exit_status_ = exit_code; else throw system_error(ec, "running failed"); @@ -285,7 +285,7 @@ struct basic_process { native_exit_code_type exit_code ; auto r = process_handle_.running(exit_code, ec); - if (!ec) + if (!ec && !r) exit_status_ = exit_code; return r; } diff --git a/include/boost/process/v2/stdio.hpp b/include/boost/process/v2/stdio.hpp index 73c95824e..fa6a7966d 100644 --- a/include/boost/process/v2/stdio.hpp +++ b/include/boost/process/v2/stdio.hpp @@ -13,6 +13,11 @@ #include #include +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#else +#include +#endif #if defined(BOOST_PROCESS_V2_POSIX) #include @@ -91,6 +96,30 @@ struct process_io_binding } + template + process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe & readable_pipe, + typename std::enable_if::type = 0) + { + BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2]; + BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec); + if (ec) + return ; + h = std::unique_ptr{p[1], true}; + readable_pipe.assign(p[0], ec); + } + + + template + process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe & writable_pipe, + typename std::enable_if::type = 0) + { + BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2]; + BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec); + if (ec) + return ; + h = std::unique_ptr{p[0], true}; + writable_pipe.assign(p[1], ec); + } }; typedef process_io_binding process_input_binding; @@ -105,6 +134,7 @@ struct process_io_binding constexpr static int target = Target; int fd{target}; bool fd_needs_closing{false}; + error_code ec; ~process_io_binding() { @@ -129,13 +159,56 @@ struct process_io_binding { } - error_code on_exec_setup(posix::default_launcher & launcher, + template + process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe & readable_pipe, + typename std::enable_if::type = 0) + { + BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2]; + BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec); + if (ec) + return ; + fd = p[1]; + if (::fcntl(p[0], F_SETFD, FD_CLOEXEC) == -1) + { + ec = detail::get_last_error(); + return ; + } + fd_needs_closing = true; + readable_pipe.assign(p[0], ec); + } + + + template + process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe & writable_pipe, + typename std::enable_if::type = 0) + { + BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2]; + BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec); + if (ec) + return ; + fd = p[0]; + if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1) + { + ec = detail::get_last_error(); + return ; + } + fd_needs_closing = true; + writable_pipe.assign(p[1], ec); + } + + error_code on_setup(posix::default_launcher &, + const filesystem::path &, const char * const *) + { + return ec; + } + + error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *) { if (::dup2(fd, target) == -1) - return error_code(errno, system_category()); + return get_last_error(); else - return error_code (); + return error_code(); } }; diff --git a/test/v2/process.cpp b/test/v2/process.cpp index a390e8ab3..f2e7eca56 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -291,13 +291,10 @@ BOOST_AUTO_TEST_CASE(print_same_cwd) asio::io_context ctx; asio::readable_pipe rp{ctx}; - asio::writable_pipe wp{ctx}; - asio::connect_pipe(rp, wp); // default CWD - bpv::process proc(ctx, pth, {"print-cwd"}, bpv::process_stdio{/*.in=*/{},/*.out=*/wp}); - wp.close(); + bpv::process proc(ctx, pth, {"print-cwd"}, bpv::process_stdio{/*.in=*/{},/*.out=*/rp}); std::string out; bpv::error_code ec; diff --git a/test/v2/target.cpp b/test/v2/target.cpp index 4f0903e77..d50f36e01 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -2,6 +2,7 @@ #include #include + #if defined(BOOST_PROCESS_V2_WINDOWS) #include #else @@ -35,6 +36,7 @@ int main(int argc, char * argv[]) char buf[65535]; printf(::getcwd(buf, sizeof(buf))); #endif + return 0; } else if (mode == "check-eof") { From 4fce3c818462f7de7be7b34ea7d32680de322f93 Mon Sep 17 00:00:00 2001 From: Klemens Date: Wed, 1 Jun 2022 14:24:05 +0800 Subject: [PATCH 065/397] Added popen. --- include/boost/process/v2.hpp | 1 + .../process/v2/detail/process_handle_fd.hpp | 19 +- include/boost/process/v2/popen.hpp | 396 ++++++++++++++++++ include/boost/process/v2/process.hpp | 10 +- test/v2/Jamfile.jam | 4 +- test/v2/process.cpp | 32 ++ 6 files changed, 446 insertions(+), 16 deletions(-) create mode 100644 include/boost/process/v2/popen.hpp diff --git a/include/boost/process/v2.hpp b/include/boost/process/v2.hpp index d18776e82..461deada9 100644 --- a/include/boost/process/v2.hpp +++ b/include/boost/process/v2.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index 1ae5877b2..9d7cbb23d 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -54,25 +54,24 @@ struct basic_process_handle_fd template basic_process_handle_fd(ExecutionContext &context, - typename std::enable_if< - std::is_convertible::value - >::type = 0) + typename std::enable_if< + std::is_convertible::value>::type * = nullptr) : pid_(-1), descriptor_(context) { } - basic_process_handle_fd(Executor executor) + basic_process_handle_fd(executor_type executor) : pid_(-1), descriptor_(executor) { } - basic_process_handle_fd(Executor executor, pid_type pid) + basic_process_handle_fd(executor_type executor, pid_type pid) : pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0)) { } - basic_process_handle_fd(Executor executor, pid_type pid, native_handle_type process_handle) + basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle) : pid_(pid), descriptor_(executor, process_handle) { } @@ -185,13 +184,15 @@ struct basic_process_handle_fd if (pid_ <= 0) return false; int code = 0; - int res = ::waitpid(pid_, &code, 0); + int res = ::waitpid(pid_, &code, WNOHANG); if (res == -1) ec = get_last_error(); + else if (res == 0) + return true; else ec.clear(); - if (process_is_running(res)) + if (process_is_running(code)) return true; else { diff --git a/include/boost/process/v2/popen.hpp b/include/boost/process/v2/popen.hpp new file mode 100644 index 000000000..c9c738300 --- /dev/null +++ b/include/boost/process/v2/popen.hpp @@ -0,0 +1,396 @@ +// 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_POPEN_HPP +#define BOOST_PROCESS_V2_POPEN_HPP + +#include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#include +#include +#else +#include +#include +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + + +template +struct basic_popen : basic_process +{ + /// The executor of the process + using executor_type = Executor; + + /// Rebinds the popen type to another executor. + template + struct rebind_executor + { + /// The pipe type when rebound to the specified executor. + typedef basic_popen other; + }; + + basic_popen(basic_popen &&) = default; + basic_popen& operator=(basic_popen &&) = default; + + template + basic_popen(basic_process&& lhs) + : basic_process(std::move(lhs)), + stdin_(std::move(lhs.stdin_)), stdout_(std::move(lhs.stdout_)) + { + } + + /// Create an invalid handle + explicit basic_popen(executor_type exec) : basic_process{std::move(exec)} {} + + /// Create an invalid handle + template + explicit basic_popen(ExecutionContext & context, + typename std::enable_if< + is_convertible::value, void *>::type = nullptr) + : basic_process{context} + { + } + + + + /// Construct a child from a property list and launch it. + template + explicit basic_popen( + executor_type executor, + const filesystem::path& exe, + std::initializer_list args, + Inits&&... inits) + : basic_process(executor) + { + *static_cast*>(this) = + default_process_launcher()( + this->get_executor(), exe, args, + std::forward(inits)..., + process_stdio{stdin_, stdout_} + ); + + } + /// Construct a child from a property list and launch it. + template + explicit basic_popen( + executor_type executor, + const filesystem::path& exe, + std::initializer_list args, + Inits&&... inits) + : basic_process(executor) + { + *static_cast*>(this) = + default_process_launcher()( + this->get_executor(), exe, args, + std::forward(inits)..., + process_stdio{stdin_, stdout_} + ); + } + + /// Construct a child from a property list and launch it. + template + explicit basic_popen( + executor_type executor, + const filesystem::path& exe, + Args&& args, Inits&&... inits) + : basic_process(executor) + { + *static_cast*>(this) = + default_process_launcher()( + std::move(executor), exe, args, + std::forward(inits)..., + process_stdio{stdin_, stdout_} + ); + } + + /// Construct a child from a property list and launch it. + template + explicit basic_popen( + ExecutionContext & context, + typename std::enable_if< + std::is_convertible::value, + const filesystem::path&>::type exe, + std::initializer_list args, + Inits&&... inits) + : basic_process(context) + { + *static_cast*>(this) = + default_process_launcher()( + this->get_executor(), exe, args, + std::forward(inits)..., + process_stdio{stdin_, stdout_} + ); + } + + /// Construct a child from a property list and launch it. + template + explicit basic_popen( + ExecutionContext & context, + typename std::enable_if< + std::is_convertible::value, + const filesystem::path&>::type exe, + Args&& args, Inits&&... inits) + : basic_process(context) + { + *static_cast*>(this) = + default_process_launcher()( + this->get_executor(), exe, args, + std::forward(inits)..., + process_stdio{stdin_, stdout_} + ); + } + using stdin_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe; + using stdout_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe; + + stdin_type & get_stdin() {return stdin_; } + stdout_type & get_stdout() {return stdout_; } + + const stdin_type & get_stdin() const {return stdin_; } + const stdout_type & get_stdout() const {return stdout_; } + + /// Write some data to the pipe. + /** + * This function is used to write data to the pipe. The function call will + * block until one or more bytes of the data has been written successfully, + * or until an error occurs. + * + * @param buffers One or more data buffers to be written to the pipe. + * + * @returns The number of bytes written. + * + * @throws boost::system::system_error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * pipe.write_some(boost::asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + return stdin_.write_some(buffers); + } + + /// Write some data to the pipe. + /** + * This function is used to write data to the pipe. The function call will + * block until one or more bytes of the data has been written successfully, + * or until an error occurs. + * + * @param buffers One or more data buffers to be written to the pipe. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + boost::system::error_code& ec) + { + return stdin_.write_some(buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the pipe. It is an + * initiating function for an @ref asynchronous_operation, and always returns + * immediately. + * + * @param buffers One or more data buffers to be written to the pipe. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the completion handler is called. + * + * @param token The @ref completion_token that will be used to produce a + * completion handler, which will be called when the write completes. + * Potential completion tokens include @ref use_future, @ref use_awaitable, + * @ref yield_context, or a function object with the correct completion + * signature. The function signature of the completion handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the completion handler will not be invoked from within this function. + * On immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Completion Signature + * @code void(boost::system::error_code, std::size_t) @endcode + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * pipe.async_write_some(boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(WriteToken, + void (boost::system::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(WriteToken) token + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return stdin_.async_write_some(buffers, std::forward(token)); + } + + + /// Read some data from the pipe. + /** + * This function is used to read data from the pipe. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws boost::system::system_error Thrown on failure. An error code of + * boost::asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * basic_readable_pipe.read_some(boost::asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + return stdout_.read_some(buffers); + } + + /// Read some data from the pipe. + /** + * This function is used to read data from the pipe. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + boost::system::error_code& ec) + { + return stdout_.read_some(buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the pipe. It is an + * initiating function for an @ref asynchronous_operation, and always returns + * immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the completion handler is called. + * + * @param token The @ref completion_token that will be used to produce a + * completion handler, which will be called when the read completes. + * Potential completion tokens include @ref use_future, @ref use_awaitable, + * @ref yield_context, or a function object with the correct completion + * signature. The function signature of the completion handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the completion handler will not be invoked from within this function. + * On immediate completion, invocation of the handler will be performed in a + * manner equivalent to using boost::asio::post(). + * + * @par Completion Signature + * @code void(boost::system::error_code, std::size_t) @endcode + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * basic_readable_pipe.async_read_some( + * boost::asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadToken, + void (boost::system::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(ReadToken) token + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) + { + return stdout_.async_read_some(buffers, std::forward(token)); + } + + + + private: + stdin_type stdin_ {basic_process::get_executor()}; + stdout_type stdout_{basic_process::get_executor()}; +}; + +using popen = basic_popen<>; + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_POPEN_HPP diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index ff67e9649..9eb3912d2 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -154,11 +154,11 @@ struct basic_process } /// Attach to an existing process - explicit basic_process(executor_type exec, pid_type pid) : process_handle_{std::move(exec), pid} {} + explicit basic_process(executor_type exec, pid_type pid) : process_handle_(std::move(exec), pid) {} /// Attach to an existing process and the internal handle explicit basic_process(executor_type exec, pid_type pid, native_handle_type native_handle) - : process_handle_{std::move(exec), pid, native_handle} {} + : process_handle_(std::move(exec), pid, native_handle) {} /// Create an invalid handle explicit basic_process(executor_type exec) : process_handle_{std::move(exec)} {} @@ -169,7 +169,7 @@ struct basic_process typename std::enable_if< std::is_convertible::value, void *>::type = nullptr) - : process_handle_{context, pid} {} + : process_handle_(context, pid) {} /// Attach to an existing process and the internal handle template @@ -177,7 +177,7 @@ struct basic_process typename std::enable_if< std::is_convertible::value, void *>::type = nullptr) - : process_handle_{context, pid, native_handle} {} + : process_handle_(context, pid, native_handle) {} /// Create an invalid handle template @@ -185,7 +185,7 @@ struct basic_process typename std::enable_if< is_convertible::value, void *>::type = nullptr) - : process_handle_{context} {} + : process_handle_(context) {} diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index b2371adaa..c19c3c71a 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -29,9 +29,9 @@ project : requirements import testing ; -alias filesystem : /boost//filesystem ; +alias filesystem : /boost//filesystem ; -exe target : target.cpp filesystem : +exe target : target.cpp : off windows:shell32 windows:Ntdll ; diff --git a/test/v2/process.cpp b/test/v2/process.cpp index f2e7eca56..6deed6ce2 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -14,6 +14,7 @@ #endif // Test that header file is self-contained. +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -309,6 +311,36 @@ BOOST_AUTO_TEST_CASE(print_same_cwd) BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code()); } + +BOOST_AUTO_TEST_CASE(popen) +{ + using boost::unit_test::framework::master_test_suite; + const auto pth = master_test_suite().argv[1]; + + asio::io_context ctx; + + asio::readable_pipe rp{ctx}; + + + // default CWD + bpv::popen proc(ctx, pth, {"echo"}); + + asio::write(proc, asio::buffer("FOOBAR")); + + proc.get_stdin().close(); + + std::string res; + boost::system::error_code ec; + std::size_t n = asio::read(proc, asio::dynamic_buffer(res), ec); + res.resize(n - 1); + BOOST_CHECK_EQUAL(ec, asio::error::eof); + // remove EOF + BOOST_CHECK_EQUAL(res, "FOOBAR"); + + proc.wait(); + BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code()); +} + BOOST_AUTO_TEST_CASE(print_other_cwd) { using boost::unit_test::framework::master_test_suite; From 76c393fb8e6e0f14e36353f6fbf03c7d1e98e22e Mon Sep 17 00:00:00 2001 From: Klemens Date: Wed, 1 Jun 2022 16:07:38 +0800 Subject: [PATCH 066/397] Added execute & async_execute. --- include/boost/process/v2.hpp | 1 + include/boost/process/v2/execute.hpp | 97 ++++++++++++++++++++++++++++ test/v2/process.cpp | 7 +- 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 include/boost/process/v2/execute.hpp diff --git a/include/boost/process/v2.hpp b/include/boost/process/v2.hpp index 461deada9..c3f54bd91 100644 --- a/include/boost/process/v2.hpp +++ b/include/boost/process/v2.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/include/boost/process/v2/execute.hpp b/include/boost/process/v2/execute.hpp new file mode 100644 index 000000000..6b26d8a4b --- /dev/null +++ b/include/boost/process/v2/execute.hpp @@ -0,0 +1,97 @@ +// 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_EXECUTE_HPP +#define BOOST_PROCESS_V2_EXECUTE_HPP + +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#else +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +template +inline int execute(basic_process proc) +{ + return proc.wait(); +} + +template +inline int execute(basic_process proc, error_code & ec) +{ + return proc.wait(ec); +} + +namespace detail +{ + +template +struct execute_op +{ + std::unique_ptr> proc; + + struct cancel + { + using cancellation_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_type; + basic_process * proc; + cancel(basic_process * proc) : proc(proc) {} + + void operator()(cancellation_type tp) + { + error_code ign; + if ((tp & cancellation_type::total) != cancellation_type::none) + proc->interrupt(ign); + else if ((tp & cancellation_type::partial) != cancellation_type::none) + proc->request_exit(ign); + else if ((tp & cancellation_type::terminal) != cancellation_type::none) + proc->terminate(ign); + } + }; + + template + void operator()(Self && self) + { + self.reset_cancellation_state(); + BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_slot s = self.get_cancellation_state().slot(); + if (s.is_connected()) + s.emplace(proc.get()); + + proc->async_wait( + BOOST_PROCESS_V2_ASIO_NAMESPACE::bind_cancellation_slot( + BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_slot(), + std::move(self))); + } + + template + void operator()(Self && self, error_code ec, int res) + { + self.get_cancellation_state().slot().clear(); + self.complete(ec, res); + } +}; + +} + + +template +inline +BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, int)) +async_execute(basic_process proc, + WaitHandler && handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor)) +{ + std::unique_ptr> pro_(new basic_process(std::move(proc))); + auto exec = proc.get_executor(); + return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( + detail::execute_op{std::move(pro_)}, handler, exec); +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_EXECUTE_HPP diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 6deed6ce2..2e266b4fb 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -76,7 +77,7 @@ BOOST_AUTO_TEST_CASE(exit_code_sync) boost::asio::io_context ctx; BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "0"}).wait(), 0); - BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "1"}).wait(), 1); + BOOST_CHECK_EQUAL(bpv::execute(bpv::process(ctx, pth, {"exit-code", "1"})), 1); std::vector args = {"exit-code", "2"}; BOOST_CHECK_EQUAL(bpv::default_process_launcher()(ctx, pth, args).wait(), 2); args[1] = "42"; @@ -101,7 +102,9 @@ BOOST_AUTO_TEST_CASE(exit_code_async) bpv::process proc4(ctx, pth, {"exit-code", "42"}); proc1.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); - proc2.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 1);}); + bpv::async_execute( + bpv::process(ctx, pth, {"exit-code", "1"}), + [&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 1);}); proc3.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 2);}); proc4.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 42);}); ctx.run(); From 3893a96c6e428260b78bb6c752305cf10cb67ba7 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 2 Jun 2022 04:01:24 +0800 Subject: [PATCH 067/397] Added hashs for environment. --- include/boost/process/v2/cstring_ref.hpp | 17 +-- include/boost/process/v2/default_launcher.hpp | 8 ++ .../process/v2/detail/environment_win.hpp | 12 ++ include/boost/process/v2/environment.hpp | 105 ++++++++++++++++++ test/v2/environment.cpp | 5 - 5 files changed, 134 insertions(+), 13 deletions(-) diff --git a/include/boost/process/v2/cstring_ref.hpp b/include/boost/process/v2/cstring_ref.hpp index 7cfcfd7ef..da141d100 100644 --- a/include/boost/process/v2/cstring_ref.hpp +++ b/include/boost/process/v2/cstring_ref.hpp @@ -27,23 +27,27 @@ BOOST_CONSTEXPR static const char32_t* null_char_(char32_t) {return U"";} BOOST_CONSTEXPR static const char8_t* null_char_(char8_t) {return u8"";} #endif - } #if defined(BOOST_PROCESS_V2_STANDALONE) using std::basic_string_view; using std:: string_view; using std:: wstring_view; -using std::u16string_view; -using std::u32string_view; #else using boost::basic_string_view; using boost:: string_view; using boost:: wstring_view; -using boost::u16string_view; -using boost::u32string_view; #endif + +/// Small wrapper for a null-terminated string that can be directly passed to C APIS +/** This ref can only be modified by moving the front pointer. It does not store the + * size, but can detect values that can directly be passed to system APIs. + * + * It can be constructed from a `char*` pointer or any class that has a `c_str()` + * member function, e.g. std::string or boost::static_string. + * + */ template> struct basic_cstring_ref { @@ -209,9 +213,6 @@ operator<<(std::basic_ostream& os, return os << static_cast>(str); } -// Forward declaration of Boost.ContainerHash function -template std::size_t hash_range(It, It); - template std::size_t hash_value(basic_string_view s) { return boost::hash_range(s.begin(), s.end()); diff --git a/include/boost/process/v2/default_launcher.hpp b/include/boost/process/v2/default_launcher.hpp index 819129aae..df9a57807 100644 --- a/include/boost/process/v2/default_launcher.hpp +++ b/include/boost/process/v2/default_launcher.hpp @@ -26,6 +26,11 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE +#if defined(GENERATING_DOCUMENTATION) + +typedef implementation-typed default_process_launcher; + +#else #if defined(BOOST_PROCESS_V2_WINDOWS) typedef windows::default_launcher default_process_launcher; #else @@ -37,6 +42,9 @@ typedef posix::default_launcher default_process_launcher; #endif +#endif + + BOOST_PROCESS_V2_END_NAMESPACE #if defined(BOOST_PROCESS_V2_HEADER_ONLY) diff --git a/include/boost/process/v2/detail/environment_win.hpp b/include/boost/process/v2/detail/environment_win.hpp index 795aef138..3ddd419e8 100644 --- a/include/boost/process/v2/detail/environment_win.hpp +++ b/include/boost/process/v2/detail/environment_win.hpp @@ -137,6 +137,18 @@ struct key_char_traits } }; +namespace detail +{ + + +template +std::size_t hash_step(std::size_t prev, Char c, key_char_traits) +{ + return prev ^ (key_char_traits::to_lower(c) << 1); +} + + +} template using value_char_traits = std::char_traits; diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index 1ce41d5e7..b23e47658 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -437,6 +438,44 @@ inline value_view key_value_pair_view::get<1u>() const return value(); } +namespace detail +{ + +template +std::size_t hash_step(std::size_t prev, Char c, std::char_traits) +{ + return prev ^ (c << 1); +} + +} + +inline std::size_t hash_value(const key_view & value) +{ + std::size_t hash = 0u; + for (auto c = value.data(); *c != *v2::detail::null_char_(*c); c++) + hash = detail::hash_step(hash, *c, key_view::traits_type{}); + return hash ; +} + + +inline std::size_t hash_value(const BOOST_PROCESS_V2_NAMESPACE::environment::value_view & value) +{ + std::size_t hash = 0u; + for (auto c = value.data(); *c != *v2::detail::null_char_(*c); c++) + hash = detail::hash_step(hash, *c, value_view::traits_type{}); + return hash ; +} + + +inline std::size_t hash_value(const BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view & value) +{ + std::size_t hash = 0u; + for (auto c = value.data(); *c != *v2::detail::null_char_(*c); c++) + hash = detail::hash_step(hash, *c, key_value_pair_view::traits_type{}); + return hash ; +} + + struct key { using value_type = char_type; @@ -1586,8 +1625,74 @@ struct process_environment }; + BOOST_PROCESS_V2_END_NAMESPACE + +namespace std +{ + +template<> +struct hash +{ + std::size_t operator()( BOOST_PROCESS_V2_NAMESPACE::environment::key_view kv) const noexcept + { + return BOOST_PROCESS_V2_NAMESPACE::environment::hash_value(kv); + } +}; + + +template<> +struct hash +{ + std::size_t operator()( BOOST_PROCESS_V2_NAMESPACE::environment::value_view kv) const noexcept + { + return BOOST_PROCESS_V2_NAMESPACE::environment::hash_value(kv); + } +}; + +template<> +struct hash +{ + std::size_t operator()( BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view kv) const noexcept + { + return BOOST_PROCESS_V2_NAMESPACE::environment::hash_value(kv); + } +}; + + +template<> +struct hash +{ + std::size_t operator()( BOOST_PROCESS_V2_NAMESPACE::environment::key_view kv) const noexcept + { + return BOOST_PROCESS_V2_NAMESPACE::environment::hash_value(kv); + } +}; + + +template<> +struct hash +{ + std::size_t operator()( BOOST_PROCESS_V2_NAMESPACE::environment::value_view kv) const noexcept + { + return BOOST_PROCESS_V2_NAMESPACE::environment::hash_value(kv); + } +}; + +template<> +struct hash +{ + std::size_t operator()( BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view kv) const noexcept + { + return BOOST_PROCESS_V2_NAMESPACE::environment::hash_value(kv); + } +}; + +} + + + #if defined(BOOST_PROCESS_V2_HEADER_ONLY) #include diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index 1e923783e..16e4a6280 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -20,9 +20,7 @@ namespace bpe = boost::process::v2::environment; BOOST_AUTO_TEST_CASE(environment) { -#if defined(BOOST_PROCESS_V2_WINDOWS) -#endif for (const auto & elem : bpe::get("PATH")) BOOST_CHECK(std::find(elem.begin(), elem.end(), bpe::delimiter) == elem.end()); @@ -45,9 +43,6 @@ BOOST_AUTO_TEST_CASE(environment) BOOST_CHECK(bpe::get(key1) == "some test string"); bpe::set(key2, "some test string"); -#if defined(BOOST_PROCESS_V2_POSIX) - //bpe::unset(key1); -#endif bpe::get(key1, ec); BOOST_CHECK(!ec); From d63d502b40b0f771752d6ee787050d38886b2792 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 3 Jun 2022 11:03:30 +0800 Subject: [PATCH 068/397] Added v2 examples and some doc comments. --- example/v2/Jamfile.jam | 17 ++++++ example/v2/intro.cpp | 39 +++++++++++++ example/v2/intro_popen.cpp | 36 ++++++++++++ include/boost/process/v2/posix/bind_fd.hpp | 55 ++++++++++++++++++- .../process/v2/posix/default_launcher.hpp | 4 +- .../v2/posix/fork_and_forget_launcher.hpp | 2 +- .../process/v2/posix/pdfork_launcher.hpp | 3 +- .../boost/process/v2/posix/vfork_launcher.hpp | 2 +- .../process/v2/windows/as_user_launcher.hpp | 3 +- .../process/v2/windows/creation_flags.hpp | 6 ++ .../process/v2/windows/default_launcher.hpp | 10 +++- .../boost/process/v2/windows/show_window.hpp | 1 + .../v2/windows/with_logon_launcher.hpp | 2 +- .../v2/windows/with_token_launcher.hpp | 2 +- test/v2/Jamfile.jam | 2 +- 15 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 example/v2/Jamfile.jam create mode 100644 example/v2/intro.cpp create mode 100644 example/v2/intro_popen.cpp diff --git a/example/v2/Jamfile.jam b/example/v2/Jamfile.jam new file mode 100644 index 000000000..26da639a2 --- /dev/null +++ b/example/v2/Jamfile.jam @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Klemens 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) + +project : requirements + ../../.. + msvc:_SCL_SECURE_NO_WARNINGS + windows:WIN32_LEAN_AND_MEAN +; + +import testing ; + +alias filesystem : /boost//filesystem : static ; + +exe intro : intro.cpp filesystem ; +exe intro_popen : intro_popen.cpp filesystem ; diff --git a/example/v2/intro.cpp b/example/v2/intro.cpp new file mode 100644 index 000000000..815792250 --- /dev/null +++ b/example/v2/intro.cpp @@ -0,0 +1,39 @@ +// Copyright (c) 2022Klemens Morgernstern +// +// 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) + +//[intro +#include + +#include +#include +#include + +#include +#include + +namespace proc = boost::process::v2; +namespace asio = boost::asio; + + +int main() +{ + asio::io_context ctx; + asio::readable_pipe p{ctx}; + + const auto exe = proc::environment::find_executable("gcc"); + + proc::process c{ctx, exe, {"--version"}, proc::process_stdio{nullptr, p}}; + + std::string line; + boost::system::error_code ec; + + auto sz = asio::read(p, asio::dynamic_buffer(line), ec); + assert(ec == asio::error::eof); + + std::cout << "Gcc version: '" << line << "'" << std::endl; + + c.wait(); +} +//] diff --git a/example/v2/intro_popen.cpp b/example/v2/intro_popen.cpp new file mode 100644 index 000000000..47e94bb96 --- /dev/null +++ b/example/v2/intro_popen.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2022Klemens Morgernstern +// +// 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) + +//[intro +#include + +#include +#include +#include + +#include +#include + +namespace proc = boost::process::v2; +namespace asio = boost::asio; + + +int main() +{ + asio::io_context ctx; + const auto exe = proc::environment::find_executable("gcc"); + proc::popen c{ctx, exe, {"--version"}}; + + std::string line; + boost::system::error_code ec; + + auto sz = asio::read(c, asio::dynamic_buffer(line), ec); + assert(ec == asio::error::eof); + + std::cout << "Gcc version: '" << line << "'" << std::endl; + + c.wait(); +} +//] diff --git a/include/boost/process/v2/posix/bind_fd.hpp b/include/boost/process/v2/posix/bind_fd.hpp index fe6cc069b..1530210eb 100644 --- a/include/boost/process/v2/posix/bind_fd.hpp +++ b/include/boost/process/v2/posix/bind_fd.hpp @@ -15,6 +15,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace posix { +/// Utility class to bind a file descriptor to an explicit file descriptor for the child process. struct bind_fd { int target; @@ -28,19 +29,69 @@ struct bind_fd } bind_fd() = delete; + /// Inherit file descriptor with the same value. + /** + * This will pass descriptor 42 as 42 to the child process: + * @code + * process p{"test", {}, posix::bind_fd(42)}; + * @endcode + */ bind_fd(int target) : target(target), fd(target) {} + + /// Inherit an asio io-object as a given file descriptor to the child process. + /** + * This will pass the tcp::socket, as 42 to the child process: + * @code + * extern tcp::socket sock; + * process p{"test", {}, posix::bind_fd(42, sock)}; + * @endcode + */ + template - bind_fd(Stream && str, decltype(std::declval().native_handle()) = -1) - : bind_fd(str.native_handle()) + bind_fd(int target, Stream && str, decltype(std::declval().native_handle()) = -1) + : bind_fd(target, str.native_handle()) {} + + /// Inherit a `FILE` as a given file descriptor to the child process. + /** + * This will pass the given `FILE*`, as 42 to the child process: + * @code + * process p{"test", {}, posix::bind_fd(42, stderr)}; + * @endcode + */ bind_fd(int target, FILE * f) : bind_fd(target, fileno(f)) {} + + /// Inherit a file descriptor with as a differnet value. + /** + * This will pass 24 as 42 to the child process: + * @code + * process p{"test", {}, posix::bind_fd(42, 24)}; + * @endcode + */ bind_fd(int target, int fd) : target(target), fd(fd) {} + + /// Inherit a null device as a set descriptor. + /** + * This will pass 24 as 42 to the child process: + * @code + * process p{"test", {}, posix::bind_fd(42, nullptr)}; + * @endcode + */ bind_fd(int target, std::nullptr_t) : bind_fd(target, filesystem::path("/dev/null")) {} + + /// Inherit a newly openedfile as a set descriptor. + /** + * This will pass 24 as 42 to the child process: + * @code + * process p{"test", {}, posix::bind_fd(42, "extra-output.txt")}; + * @endcode + */ bind_fd(int target, const filesystem::path & pth, int flags = O_RDWR | O_CREAT) : target(target), fd(::open(pth.c_str(), flags, 0660)), fd_needs_closing(true) { } + /// Implementation of the initialization function. error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *) { if (::dup2(fd, target) == -1) diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index be26e4524..4af301987 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -289,8 +289,10 @@ inline void on_exec_error(Launcher & launcher, const filesystem::path &executabl /// The default launcher for processes on windows. struct default_launcher { + /// The pointer to the environment forwarded to the subprocess. const char * const * env = ::environ; - int pid; + /// The pid of the subprocess - will be assigned after fork. + int pid = -1; default_launcher() = default; diff --git a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp index 4c2a2a56b..383e33af0 100644 --- a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp +++ b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp @@ -12,7 +12,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace posix { -/// The default launcher for processes on windows. +/// A posix fork launcher that ignores errors after `fork`. struct fork_and_forget_launcher : default_launcher { fork_and_forget_launcher() = default; diff --git a/include/boost/process/v2/posix/pdfork_launcher.hpp b/include/boost/process/v2/posix/pdfork_launcher.hpp index 10c3b7687..fad30ece9 100644 --- a/include/boost/process/v2/posix/pdfork_launcher.hpp +++ b/include/boost/process/v2/posix/pdfork_launcher.hpp @@ -15,9 +15,10 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace posix { -/// The default launcher for processes on windows. +/// A launcher using `pdfork`. Default on FreeBSD struct pdfork_launcher : default_launcher { + /// The file descriptor of the subprocess. Set after fork. int fd; pdfork_launcher() = default; diff --git a/include/boost/process/v2/posix/vfork_launcher.hpp b/include/boost/process/v2/posix/vfork_launcher.hpp index b00500fa9..48fe78697 100644 --- a/include/boost/process/v2/posix/vfork_launcher.hpp +++ b/include/boost/process/v2/posix/vfork_launcher.hpp @@ -14,7 +14,7 @@ namespace posix { -/// The default launcher for processes on windows. +/// A launcher using vfork instead of fork. struct vfork_launcher : default_launcher { vfork_launcher() = default; diff --git a/include/boost/process/v2/windows/as_user_launcher.hpp b/include/boost/process/v2/windows/as_user_launcher.hpp index 604852eca..cf1c8b712 100644 --- a/include/boost/process/v2/windows/as_user_launcher.hpp +++ b/include/boost/process/v2/windows/as_user_launcher.hpp @@ -18,9 +18,10 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace windows { -/// The default launcher for processes on windows. +/// A windows launcher using CreateProcessAsUser instead of CreateProcess struct as_user_launcher : default_launcher { + /// The token to be used in CreateProcessAsUser. HANDLE token; as_user_launcher(HANDLE token = INVALID_HANDLE_VALUE) : token(token) {} diff --git a/include/boost/process/v2/windows/creation_flags.hpp b/include/boost/process/v2/windows/creation_flags.hpp index 1987dd8db..89f08d819 100644 --- a/include/boost/process/v2/windows/creation_flags.hpp +++ b/include/boost/process/v2/windows/creation_flags.hpp @@ -17,6 +17,11 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace windows { + +/// An initializers to add to the dwFlags in the startup-info +/** + * @tparam Flags The flags to be set. + */ template struct process_creation_flags { @@ -31,6 +36,7 @@ struct process_creation_flags }; }; +/// A flag to create a new process group. Necessary to allow interupts for the subproces. constexpr static process_creation_flags create_new_process_group; } diff --git a/include/boost/process/v2/windows/default_launcher.hpp b/include/boost/process/v2/windows/default_launcher.hpp index dd78881a9..aa8d47891 100644 --- a/include/boost/process/v2/windows/default_launcher.hpp +++ b/include/boost/process/v2/windows/default_launcher.hpp @@ -202,19 +202,27 @@ namespace windows /// The default launcher for processes on windows. struct default_launcher { + //// The process_attributes passed to CreateProcess SECURITY_ATTRIBUTES * process_attributes = nullptr; + //// The thread_attributes passed to CreateProcess SECURITY_ATTRIBUTES * thread_attributes = nullptr; + /// The bInheritHandles option. Needs to be set to true by any initializers using handles. bool inherit_handles = false; - DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT }; + /// The creation flags of the process. Initializers may add to them; extended startupinfo is assumed. + DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT}; + /// A pointer to the subprocess environment. void * environment = nullptr; + /// The startup director. An empty path will get ignored. filesystem::path current_directory{}; + /// The full startup info passed to CreateProcess STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}, nullptr}; + /// The process_information that gets assigned after a call to CreateProcess PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0}; template diff --git a/include/boost/process/v2/windows/show_window.hpp b/include/boost/process/v2/windows/show_window.hpp index 31d61ebcf..1c75b79b8 100644 --- a/include/boost/process/v2/windows/show_window.hpp +++ b/include/boost/process/v2/windows/show_window.hpp @@ -17,6 +17,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace windows { +/// A templated initializer to add wShowWindow flags. template struct process_show_window { diff --git a/include/boost/process/v2/windows/with_logon_launcher.hpp b/include/boost/process/v2/windows/with_logon_launcher.hpp index 08244e9c5..c18a02631 100644 --- a/include/boost/process/v2/windows/with_logon_launcher.hpp +++ b/include/boost/process/v2/windows/with_logon_launcher.hpp @@ -17,7 +17,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace windows { -/// The default launcher for processes on windows. +/// A windows launcher using CreateProcessWithLogon instead of CreateProcess struct with_logon_launcher : default_launcher { std::wstring username, domain, password; diff --git a/include/boost/process/v2/windows/with_token_launcher.hpp b/include/boost/process/v2/windows/with_token_launcher.hpp index 040eefaf5..c3e8383c5 100644 --- a/include/boost/process/v2/windows/with_token_launcher.hpp +++ b/include/boost/process/v2/windows/with_token_launcher.hpp @@ -17,7 +17,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace windows { -/// The default launcher for processes on windows. +/// A windows launcher using CreateProcessWithToken instead of CreateProcess struct with_token_launcher : default_launcher { HANDLE token; diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index c19c3c71a..1b45e11b3 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -29,7 +29,7 @@ project : requirements import testing ; -alias filesystem : /boost//filesystem ; +alias filesystem : /boost//filesystem : static ; exe target : target.cpp : off windows:shell32 From 62d40caddd3f91bc483acff1719db39a975a5cd3 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 3 Jun 2022 11:54:34 +0800 Subject: [PATCH 069/397] Added sleep test. --- .drone.star | 2 +- test/v2/process.cpp | 28 ++++++++++++++++++++-------- test/v2/target.cpp | 7 +++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/.drone.star b/.drone.star index 3411f507a..9269066e6 100644 --- a/.drone.star +++ b/.drone.star @@ -25,7 +25,7 @@ def main(ctx): linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv), # Next, a standard list of tests from boost-ci: linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), - linux_cxx("tsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'tsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_TSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_JOB_UUID': 'da4b9237ba'}, globalenv=globalenv), + # linux_cxx("tsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'tsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_TSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_JOB_UUID': 'da4b9237ba'}, globalenv=globalenv), linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), # a boost-ci based version of codecov. However, codecov has already been run, above. # linux_cxx("codecov", "g++-8", packages="g++-8", buildtype="codecov", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'codecov.io', 'B2_CXXSTD': '11', 'B2_TOOLSET': 'gcc-8', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_JOB_UUID': 'b6589fc6ab', "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv), diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 2e266b4fb..749337744 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -84,6 +84,10 @@ BOOST_AUTO_TEST_CASE(exit_code_sync) auto proc = bpv::default_process_launcher()(ctx, pth, args); BOOST_CHECK_EQUAL(proc.wait(), 42); + BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"sleep", "100"}).wait(), 0); + BOOST_CHECK_EQUAL(bpv::execute(bpv::process(ctx, pth, {"sleep", "100"})), 0); + + } BOOST_AUTO_TEST_CASE(exit_code_async) @@ -97,9 +101,11 @@ BOOST_AUTO_TEST_CASE(exit_code_async) int called = 0; bpv::process proc1(ctx, pth, {"exit-code", "0"}); - bpv::process proc2(ctx, pth, {"exit-code", "1"}); + bpv::process proc3(ctx, pth, {"exit-code", "2"}); bpv::process proc4(ctx, pth, {"exit-code", "42"}); + bpv::process proc5(ctx, pth, {"sleep", "100"});; + proc1.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); bpv::async_execute( @@ -107,8 +113,13 @@ BOOST_AUTO_TEST_CASE(exit_code_async) [&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 1);}); proc3.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 2);}); proc4.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 42);}); + proc5.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); + bpv::async_execute( + bpv::process(ctx, pth, {"sleep", "100"}), + [&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); + ctx.run(); - BOOST_CHECK_EQUAL(called, 4); + BOOST_CHECK_EQUAL(called, 6); } @@ -297,7 +308,6 @@ BOOST_AUTO_TEST_CASE(print_same_cwd) asio::readable_pipe rp{ctx}; - // default CWD bpv::process proc(ctx, pth, {"print-cwd"}, bpv::process_stdio{/*.in=*/{},/*.out=*/rp}); @@ -347,7 +357,7 @@ BOOST_AUTO_TEST_CASE(popen) BOOST_AUTO_TEST_CASE(print_other_cwd) { using boost::unit_test::framework::master_test_suite; - const auto pth = master_test_suite().argv[1]; + const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]); asio::io_context ctx; @@ -355,10 +365,12 @@ BOOST_AUTO_TEST_CASE(print_other_cwd) asio::writable_pipe wp{ctx}; asio::connect_pipe(rp, wp); - auto tmp = bpv::filesystem::canonical(bpv::filesystem::temp_directory_path()); + auto target = bpv::filesystem::canonical(bpv::filesystem::temp_directory_path()); // default CWD - bpv::process proc(ctx, pth, {"print-cwd"}, bpv::process_stdio{/*.in=*/{}, /*.out=*/wp}, bpv::process_start_dir(tmp)); + bpv::process proc(ctx, pth, {"print-cwd"}, + bpv::process_stdio{/*.in=*/{}, /*.out=*/wp}, + bpv::process_start_dir(target)); wp.close(); std::string out; @@ -367,8 +379,8 @@ BOOST_AUTO_TEST_CASE(print_other_cwd) auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); BOOST_CHECK(sz != 0); BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); - BOOST_CHECK_MESSAGE(bpv::filesystem::path(out) == tmp, - bpv::filesystem::path(out) << " != " << tmp); + BOOST_CHECK_MESSAGE(bpv::filesystem::path(out) == target, + bpv::filesystem::path(out) << " != " << target); proc.wait(); BOOST_CHECK_MESSAGE(proc.exit_code() == 0, proc.exit_code() << " from " << proc.native_exit_code()); diff --git a/test/v2/target.cpp b/test/v2/target.cpp index d50f36e01..852f33f1d 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -1,5 +1,6 @@ #include #include +#include #include @@ -15,6 +16,12 @@ int main(int argc, char * argv[]) std::string mode = argv[1]; if (mode == "exit-code") return std::stoi(argv[2]); + else if (mode == "sleep") + { + const auto delay = std::chrono::milliseconds(std::stoi(argv[2])); + std::this_thread::sleep_for(delay); + return 0; + } else if (mode == "print-args") for (auto i = 0; i < argc; i++) { From 6aa704c208a52e75cf916a1cde0fedf8026b2ba4 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 4 Jun 2022 12:35:20 +0800 Subject: [PATCH 070/397] Added comments --- include/boost/process/v2/default_launcher.hpp | 12 ++++++++++ include/boost/process/v2/error.hpp | 12 ++++++++++ include/boost/process/v2/execute.hpp | 23 ++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/include/boost/process/v2/default_launcher.hpp b/include/boost/process/v2/default_launcher.hpp index df9a57807..7a1ac3123 100644 --- a/include/boost/process/v2/default_launcher.hpp +++ b/include/boost/process/v2/default_launcher.hpp @@ -28,6 +28,18 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE #if defined(GENERATING_DOCUMENTATION) +/// The default launcher for processes. +/** This launcher will be used by process if a + * process is launched through the constructor: + * + * @code {.cpp} + * process proc("test", {}); + * // equivalent to + * process prod = default_launcher()("test", {}); + * @endcode + * + */ + typedef implementation-typed default_process_launcher; #else diff --git a/include/boost/process/v2/error.hpp b/include/boost/process/v2/error.hpp index f186f0bbc..abf52b94c 100644 --- a/include/boost/process/v2/error.hpp +++ b/include/boost/process/v2/error.hpp @@ -12,6 +12,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace error { +/// Errors used for utf8 <-> UCS-2 conversions. enum utf8_conv_error { insufficient_buffer = 1, @@ -22,6 +23,17 @@ extern BOOST_PROCESS_V2_DECL const error_category& get_utf8_category(); static const error_category& utf8_category = get_utf8_category(); extern BOOST_PROCESS_V2_DECL const error_category& get_exit_code_category(); + +/// An error category that can be used to interpret exit codes of subprocesses. +/** Currently not used by boost.process, but it might be in the future. + * + * void run_my_process(filesystem::path pt, error_code & ec) + * { + * process proc(pt, {}); + * ec.assign(proc.wait(), error::get_exit_code_category()); + * } + * + * */ static const error_category& exit_code_category = get_exit_code_category(); } diff --git a/include/boost/process/v2/execute.hpp b/include/boost/process/v2/execute.hpp index 6b26d8a4b..b028cffec 100644 --- a/include/boost/process/v2/execute.hpp +++ b/include/boost/process/v2/execute.hpp @@ -15,12 +15,21 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE +/** + * @brief Run a process and wait for it to complete. + * + * @tparam Executor The asio executor of the process handle + * @param proc The process to be run. + * @return int The exit code of the process + * @exception system_error An error that might have occured during the wait. + */ template inline int execute(basic_process proc) { return proc.wait(); } +/** \overload int execute(const basic_process proc) */ template inline int execute(basic_process proc, error_code & ec) { @@ -77,7 +86,19 @@ struct execute_op } - +/// Execute a process asynchronously +/** This function asynchronously for a process to complete. + * + * Cancelling the execution will signal the child process to exit + * with the following intepretations: + * + * - cancellation_type::total -> interrupt + * - cancellation_type::partial -> request_exit + * - cancellation_type::terminal -> terminate + * + * It is to note that `async_execute` will us the lowest seelected cancellation + * type. A subprocess might ignore anything not terminal. + */ template From 15984e3288ecac85784eb9c5ac7800a8c3ecdf4e Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 4 Jun 2022 12:35:29 +0800 Subject: [PATCH 071/397] Enabled freebsd --- .drone.star | 1 + 1 file changed, 1 insertion(+) diff --git a/.drone.star b/.drone.star index 9269066e6..81d020ad3 100644 --- a/.drone.star +++ b/.drone.star @@ -14,6 +14,7 @@ windowsglobalimage="cppalliance/dronevs2019" def main(ctx): return [ + #freebsd_cxx("FreeBSD", "g++10", packages="g++10", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), # A set of jobs based on the earlier .travis.yml configuration: linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb mlocate", image="cppalliance/droneubuntu2004:1", buildtype="boost_v1", buildscript="drone", environment={"GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "14", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), From 910192e2adb86286fad8c6a9adf8e9b60106dae4 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 4 Jun 2022 16:44:57 +0800 Subject: [PATCH 072/397] Added reference doc for v2. --- include/boost/process/v2/cstring_ref.hpp | 2 +- .../process/v2/detail/process_handle_fd.hpp | 20 +- .../v2/detail/process_handle_fd_or_signal.hpp | 20 +- .../v2/detail/process_handle_signal.hpp | 18 +- .../v2/detail/process_handle_windows.hpp | 20 +- include/boost/process/v2/environment.hpp | 390 ++++++++++++------ include/boost/process/v2/error.hpp | 3 +- include/boost/process/v2/exit_code.hpp | 20 + include/boost/process/v2/impl/error.ipp | 2 +- include/boost/process/v2/pid.hpp | 9 + include/boost/process/v2/popen.hpp | 49 ++- include/boost/process/v2/process.hpp | 98 ++--- include/boost/process/v2/process_handle.hpp | 110 +++++ include/boost/process/v2/start_dir.hpp | 2 + include/boost/process/v2/stdio.hpp | 40 ++ test/v2/cstring_ref.cpp | 2 - test/v2/environment.cpp | 28 +- test/v2/process.cpp | 4 +- test/v2/utf8.cpp | 15 +- 19 files changed, 643 insertions(+), 209 deletions(-) diff --git a/include/boost/process/v2/cstring_ref.hpp b/include/boost/process/v2/cstring_ref.hpp index da141d100..a3e146304 100644 --- a/include/boost/process/v2/cstring_ref.hpp +++ b/include/boost/process/v2/cstring_ref.hpp @@ -113,7 +113,7 @@ struct basic_cstring_ref BOOST_CONSTEXPR const_reference back() const {return view_[length() - 1];} BOOST_CONSTEXPR const_pointer data() const BOOST_NOEXCEPT {return view_;} BOOST_CXX14_CONSTEXPR void remove_prefix(size_type n) {view_ = view_ + n;} - BOOST_CONSTEXPR void swap(basic_cstring_ref& s) BOOST_NOEXCEPT {std::swap(view_, s.view_);} + void swap(basic_cstring_ref& s) BOOST_NOEXCEPT {std::swap(view_, s.view_);} size_type copy(value_type* s, size_type n, size_type pos = 0) const { diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index 9d7cbb23d..d7bd3def2 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -76,10 +76,26 @@ struct basic_process_handle_fd { } + basic_process_handle_fd(basic_process_handle_fd &&handle) + : pid_(handle.pid_), descriptor_(std::move(handle.descriptor_)) + { + handle.pid_ = -1; + } + template basic_process_handle_fd(basic_process_handle_fd &&handle) : pid_(handle.pid_), descriptor_(std::move(handle.descriptor_)) { + handle.pid_ = -1; + } + + + basic_process_handle_fd& operator=(basic_process_handle_fd &&handle) + { + pid_ = handle.pid_; + descriptor_ = std::move(handle.descriptor_); + handle.pid_ = -1; + return *this; } pid_type id() const @@ -179,7 +195,7 @@ struct basic_process_handle_fd detail::throw_error(ec, "terminate"); } - bool running(native_exit_code_type &exit_code, error_code ec) + bool running(native_exit_code_type &exit_code, error_code & ec) { if (pid_ <= 0) return false; @@ -218,7 +234,7 @@ struct basic_process_handle_fd return pid_ != -1; } - template BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) 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 c8224b65f..1dec8f214 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 @@ -78,10 +78,28 @@ struct basic_process_handle_fd_or_signal { } + + basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal &&handle) + : pid_(handle.pid_), descriptor_(std::move(handle.descriptor_)) + { + handle.pid_ = -1; + } + + basic_process_handle_fd_or_signal& operator=(basic_process_handle_fd_or_signal &&handle) + { + pid_ = handle.pid_; + descriptor_ = std::move(handle.descriptor_); + signal_set_ = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set(handle.get_executor(), SIGCHLD); + handle.pid_ = -1; + return *this; + } + + template basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal &&handle) : pid_(handle.pid_), descriptor_(std::move(handle.descriptor_)) { + handle.pid_ = -1; } pid_type id() const @@ -181,7 +199,7 @@ struct basic_process_handle_fd_or_signal detail::throw_error(ec, "terminate"); } - bool running(native_exit_code_type &exit_code, error_code ec) + bool running(native_exit_code_type &exit_code, error_code & ec) { if (pid_ <= 0) return false; diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index 1f766d3ac..b2928c09e 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -75,10 +75,26 @@ struct basic_process_handle_signal { } + basic_process_handle_signal(basic_process_handle_signal && handle) + : pid_(handle.pid_), signal_set_(handle.signal_set_.get_executor(), SIGCHLD) + { + handle.pid_ = -1; + } + + basic_process_handle_win(basic_process_handle_win && handle) + { + pid_ = handle.id(); + signal_set_ = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set(handle.get_executor(), SIGCHLD); + handle.pid_ = static_cast(-1); + return *this; + } + + template basic_process_handle_signal(basic_process_handle_signal && handle) : pid_(handle.pid_), signal_set_(Executor1(handle.signal_set_.get_executor()), SIGCHLD) { + handle.pid_ = -1; } pid_type id() const @@ -178,7 +194,7 @@ struct basic_process_handle_signal detail::throw_error(ec, "terminate"); } - bool running(native_exit_code_type &exit_code, error_code ec) + bool running(native_exit_code_type &exit_code, error_code & ec) { if (pid_ <= 0) return false; diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp index 64c127a1c..03df1f4da 100644 --- a/include/boost/process/v2/detail/process_handle_windows.hpp +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -87,8 +87,20 @@ struct basic_process_handle_win { } - basic_process_handle_win(basic_process_handle_win && ) = default; - basic_process_handle_win& operator=(basic_process_handle_win && ) = default; + basic_process_handle_win(basic_process_handle_win && handle) + { + pid_ = handle.id(); + handle_ = std::move(handle.handle_); + handle.pid_ = static_cast(-1); + } + + basic_process_handle_win& operator=(basic_process_handle_win && handle) + { + pid_ = handle.pid_; + handle_ = std::mopve(handle_)) + handle.pid_ = static_cast(-1); + return *this; + } ~basic_process_handle_win() { @@ -185,7 +197,7 @@ struct basic_process_handle_win detail::throw_error(ec, "terminate"); } - bool running(native_exit_code_type &exit_code, error_code ec) + bool running(native_exit_code_type &exit_code, error_code & ec) { if (!detail::check_handle_(handle_.native_handle(), ec)) return false; @@ -219,7 +231,7 @@ struct basic_process_handle_win return handle_.is_open(); } - template BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index b23e47658..e7f562630 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -35,27 +35,30 @@ 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 + * that behaviour. */ tempalte -using key_char_traits = implementation-defined ; +using key_char_traits = implementation_defined ; /// A char traits type that reflects the OS rules for string representing environment values. /** Can be an alias of std::char_traits. May only be defined for `char` and `wchar_t`. */ tempalte -using value_char_traits = implementation-defined ; +using value_char_traits = implementation_defined ; /// The character type used by the environment. Either `char` or `wchar_t`. -using char_type = implementation-defined ; +using char_type = implementation_defined ; /// The equal character in an environment string used to separate key and value. -constexpr char_type equality_sign = implementation-defined; +constexpr char_type equality_sign = implementation_defined; /// The delimiter in environemtn lists. Commonly used by the `PATH` variable. -constexpr char_type equality_sign = implementation-defined; +constexpr char_type equality_sign = implementation_defined; /// The native handle of an environment. Note that this can be an owning pointer and is generally not thread safe. -using native_handle = implementation-defined; +using native_handle = implementation_defined; #endif @@ -152,7 +155,7 @@ struct key_view std::basic_string basic_string( const Alloc& alloc = Alloc()) const { - return boost::process::v2::detail::conv_string( + return BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( value_.data(), value_.size(), alloc); } @@ -177,7 +180,7 @@ struct key_view friend std::basic_ostream& operator<<( std::basic_ostream& os, const key_view& p ) { - os << boost::process::v2::quoted(p.basic_string()); + os << BOOST_PROCESS_V2_NAMESPACE::quoted(p.basic_string()); return os; } @@ -186,7 +189,7 @@ struct key_view operator>>( std::basic_istream& is, key_view& p ) { std::basic_string t; - is >> boost::process::v2::quoted(t); + is >> BOOST_PROCESS_V2_NAMESPACE::quoted(t); p = t; return is; } @@ -240,7 +243,7 @@ struct value_view std::basic_string basic_string( const Alloc& alloc = Alloc() ) const { - return boost::process::v2::detail::conv_string( + return BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( value_.data(), value_.size(), alloc); } @@ -266,7 +269,7 @@ struct value_view friend std::basic_ostream& operator<<( std::basic_ostream& os, const value_view& p ) { - os << boost::process::v2::quoted(p.basic_string()); + os << BOOST_PROCESS_V2_NAMESPACE::quoted(p.basic_string()); return os; } @@ -275,7 +278,7 @@ struct value_view operator>>( std::basic_istream& is, value_view& p ) { std::basic_string t; - is >> boost::process::v2::quoted(t); + is >> BOOST_PROCESS_V2_NAMESPACE::quoted(t); p = t; return is; } @@ -348,7 +351,7 @@ struct key_value_pair_view std::basic_string basic_string( const Alloc& alloc = Alloc()) const { - return boost::process::v2::detail::conv_string(value_.begin(), value_.size(), alloc); + return BOOST_PROCESS_V2_NAMESPACE::detail::conv_string(value_.begin(), value_.size(), alloc); } std::string string() const {return basic_string();} @@ -397,7 +400,7 @@ struct key_value_pair_view friend std::basic_ostream& operator<<( std::basic_ostream& os, const key_value_pair_view& p ) { - os << boost::process::v2::quoted(p.basic_string()); + os << BOOST_PROCESS_V2_NAMESPACE::quoted(p.basic_string()); return os; } @@ -406,14 +409,14 @@ struct key_value_pair_view operator>>( std::basic_istream& is, key_value_pair_view& p ) { std::basic_string t; - is >> boost::process::v2::quoted(t); + is >> BOOST_PROCESS_V2_NAMESPACE::quoted(t); p = t; return is; } template - inline auto get() const -> typename conditional::type; + inline auto get() const -> typename conditional::type; const value_type * c_str() const noexcept { return value_.data(); @@ -499,20 +502,20 @@ struct key decltype(source.data()) = nullptr, decltype(source.size()) = 0u) : value_( - boost::process::v2::detail::conv_string( + BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( source.data(), source.size())) { } key(const typename conditional::value, wchar_t, char>::type * raw) - : value_(boost::process::v2::detail::conv_string( + : value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( raw, std::char_traits::type>::type>::length(raw))) { } template< class InputIt > key( InputIt first, InputIt last) - : key(std::basic_string>::value_type>(first, last)) + : key(std::basic_string::type>::value_type>(first, last)) { } @@ -528,7 +531,7 @@ struct key template< class Source > key& operator=( const Source& source ) { - value_ = boost::process::v2::detail::conv_string(source.data(), source.size()); + value_ = BOOST_PROCESS_V2_NAMESPACE::detail::conv_string(source.data(), source.size()); return *this; } @@ -540,7 +543,7 @@ struct key template< class Source > key& assign( const Source& source ) { - value_ = boost::process::v2::detail::conv_string(source.data(), source.size()); + value_ = BOOST_PROCESS_V2_NAMESPACE::detail::conv_string(source.data(), source.size()); return *this; } @@ -548,7 +551,7 @@ struct key key& assign( InputIt first, InputIt last ) { - return assign(std::basic_string>::value_type>(first, last)); + return assign(std::basic_string::type>::value_type>(first, last)); } void clear() {value_.clear();} @@ -575,7 +578,7 @@ struct key std::basic_string basic_string( const Alloc& alloc = Alloc()) const { - return boost::process::v2::detail::conv_string( + return BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( value_.data(), value_.size(), alloc); } @@ -601,7 +604,7 @@ struct key friend std::basic_ostream& operator<<( std::basic_ostream& os, const key& p ) { - os << boost::process::v2::quoted(p.basic_string()); + os << BOOST_PROCESS_V2_NAMESPACE::quoted(p.basic_string()); return os; } @@ -610,7 +613,7 @@ struct key operator>>( std::basic_istream& is, key& p ) { std::basic_string t; - is >> boost::process::v2::quoted(t); + is >> BOOST_PROCESS_V2_NAMESPACE::quoted(t); p = t; return is; } @@ -704,20 +707,20 @@ struct value value( const Source& source, decltype(source.data()) = nullptr, decltype(source.size()) = 0u) - : value_(boost::process::v2::detail::conv_string( + : value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( source.data(), source.size())) { } value(const typename conditional::value, wchar_t, char>::type * raw) - : value_(boost::process::v2::detail::conv_string( + : value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( raw, std::char_traits::type>::type>::length(raw))) { } template< class InputIt > value( InputIt first, InputIt last) - : value(std::basic_string>::value_type>(first, last)) + : value(std::basic_string::type>::value_type>(first, last)) { } @@ -733,7 +736,7 @@ struct value template< class Source > value& operator=( const Source& source ) { - value_ = boost::process::v2::detail::conv_string( + value_ = BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( source.data(), source.size); return *this; } @@ -746,7 +749,7 @@ struct value template< class Source > value& assign( const Source& source ) { - value_ = boost::process::v2::detail::conv_string( + value_ = BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( source.data(), source.size()); return *this; } @@ -754,7 +757,7 @@ struct value template< class InputIt > value& assign( InputIt first, InputIt last ) { - return assign(std::basic_string>::value_type>(first, last)); + return assign(std::basic_string::type>::value_type>(first, last)); } void push_back(const value & sv) @@ -787,7 +790,7 @@ struct value std::basic_string basic_string( const Alloc& alloc = Alloc()) const { - return boost::process::v2::detail::conv_string( + return BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( value_.data(), value_.size(),alloc); } @@ -813,7 +816,7 @@ struct value friend std::basic_ostream& operator<<( std::basic_ostream& os, const value& p ) { - os << boost::process::v2::quoted(p.basic_string()); + os << BOOST_PROCESS_V2_NAMESPACE::quoted(p.basic_string()); return os; } @@ -822,7 +825,7 @@ struct value operator>>( std::basic_istream& is, value& p ) { std::basic_string t; - is >> boost::process::v2::quoted(t); + is >> BOOST_PROCESS_V2_NAMESPACE::quoted(t); p = t; return is; } @@ -917,7 +920,7 @@ struct key_value_pair key_value_pair(key_view key, std::initializer_list> values) { const auto sz = std::accumulate(values.begin(), values.end(), - key.size(), [](std::size_t sz, const auto & str) { return sz + str.size() + 1;}); + key.size(), [](std::size_t sz, const basic_string_view & str) { return sz + str.size() + 1;}); value_.reserve(sz); value_.append(key.data(), key.size()); @@ -941,20 +944,22 @@ struct key_value_pair key_value_pair( const Source& source, decltype(source.data()) = nullptr, decltype(source.size()) = 0u) - : value_(boost::process::v2::detail::conv_string( + : value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( source.data(), source.size())) { } + template< typename Key, + typename Value > + key_value_pair( + const std::pair & kv/*, + typename std::enable_if::value && + std::is_constructible::value + >::type = 0*/) : value_(((struct key)(kv.first)).string() + equality_sign + ((struct value)(kv.second)).string()) + {} + key_value_pair(const typename conditional::value, wchar_t, char>::type * raw) - : value_(boost::process::v2::detail::conv_string( - raw, - std::char_traits::type>::type>::length(raw))) - { - } - key_value_pair(const typename conditional::value, wchar_t, char>::type * raw, - const std::locale& loc) - : value_(boost::process::v2::detail::conv_string( + : value_(BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( raw, std::char_traits::type>::type>::length(raw))) { @@ -962,7 +967,7 @@ struct key_value_pair template< class InputIt , typename std::iterator_traits::iterator_category> key_value_pair( InputIt first, InputIt last ) - : key_value_pair(std::basic_string>::value_type>(first, last)) + : key_value_pair(std::basic_string::type>::value_type>(first, last)) { } @@ -978,7 +983,7 @@ struct key_value_pair template< class Source > key_value_pair& operator=( const Source& source ) { - value_ = boost::process::v2::detail::conv_string( + value_ = BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( source.data(), source.size()); return *this; } @@ -991,7 +996,7 @@ struct key_value_pair template< class Source > key_value_pair& assign( const Source& source ) { - value_ = boost::process::v2::detail::conv_string( + value_ = BOOST_PROCESS_V2_NAMESPACE::detail::conv_string( source.data(), source.size()); return *this; } @@ -1000,7 +1005,7 @@ struct key_value_pair template< class InputIt > key_value_pair& assign( InputIt first, InputIt last ) { - return assign(std::basic_string>::value_type>(first, last)); + return assign(std::basic_string::type>::value_type>(first, last)); } void clear() {value_.clear();} @@ -1040,7 +1045,7 @@ struct key_value_pair std::basic_string basic_string( const Alloc& alloc = Alloc() ) const { - return boost::process::v2::detail::conv_string(value_.data(), value_.size(), alloc); + return BOOST_PROCESS_V2_NAMESPACE::detail::conv_string(value_.data(), value_.size(), alloc); } std::string string() const {return basic_string();} @@ -1071,7 +1076,7 @@ struct key_value_pair } const auto k = native_view().substr(0, eq); - return boost::process::v2::environment::key_view::string_view_type (k.data(), k.size()); + return BOOST_PROCESS_V2_NAMESPACE::environment::key_view::string_view_type (k.data(), k.size()); } struct value_view value() const { @@ -1089,7 +1094,7 @@ struct key_value_pair friend std::basic_ostream& operator<<( std::basic_ostream& os, const key_value_pair& p ) { - os << boost::process::v2::quoted(p.basic_string()); + os << BOOST_PROCESS_V2_NAMESPACE::quoted(p.basic_string()); return os; } @@ -1097,24 +1102,22 @@ struct key_value_pair friend std::basic_istream& operator>>( std::basic_istream& is, key_value_pair& p ) { - is >> boost::process::v2::quoted(p.value_); + is >> BOOST_PROCESS_V2_NAMESPACE::quoted(p.value_); return is; } - template - inline auto get() const - -> typename conditional::type; - const value_type * data() const {return value_.data(); } std::size_t size() const {return value_.size(); } + template + inline auto get() const + -> typename conditional::type; + private: string_type value_; }; - - template typename std::enable_if< ((std::is_same::value || std::is_same::value) && @@ -1188,6 +1191,76 @@ inline value_view key_value_pair::get<1u>() const return value(); } +} +BOOST_PROCESS_V2_END_NAMESPACE + +namespace std +{ + +template<> +struct tuple_size : integral_constant {}; + +template<> +struct tuple_element<0u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair> {using type = BOOST_PROCESS_V2_NAMESPACE::environment::key_view;}; + +template<> +struct tuple_element<1u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair> {using type = BOOST_PROCESS_V2_NAMESPACE::environment::value_view;}; + +template +inline auto get(const BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair & kvp) + -> typename std::tuple_element::type +{ + return kvp.get(); +} + +template<> +struct tuple_size : integral_constant {}; + +template<> +struct tuple_element<0u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view> +{ + using type = BOOST_PROCESS_V2_NAMESPACE::environment::key_view; +}; + +template<> +struct tuple_element<1u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view> +{ + using type = BOOST_PROCESS_V2_NAMESPACE::environment::value_view; +}; + +template +inline auto get(BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view kvp) + -> typename std::tuple_element::type +{ + return kvp.get(); +} + +} + +BOOST_PROCESS_V2_BEGIN_NAMESPACE +namespace environment +{ + + +/// A view object for the current environment of this process. +/** + * The view might (windows) or might not (posix) be owning; + * if it owns it will deallocate the on destruction, like a unique_ptr. + * + * Note that accessing the environment in this way is not thread-safe. + * + * @code + * + * void dump_my_env(current_view env = current()) + * { + * for (auto & [k, v] : env) +* std::cout << k.string() << " = " << v.string() << std::endl; + * } + * + * @endcode + * + * + */ struct current_view { using native_handle_type = environment::native_handle_type; @@ -1243,52 +1316,82 @@ struct current_view detail::native_handle_deleter> handle_{environment::detail::load_native_handle()}; }; +/// Obtain a handle to the current environment inline current_view current() {return current_view();} +/// Find the home folder in an environment-like type. +/** + * @param env The environment to search. Defaults to the current environment of this process + * + * The environment type passed in must be a range with value T that fulfills the following requirements: + * + * For `T value` + * + * - std::get<0>(value) must return a type comparable to `key_view`. + * - std::get<1>(value) must return a type convertible to filesystem::path. + * + * @return A filesystem::path to the home directory or an empty path if it cannot be found. + * + */ template -inline boost::process::v2::filesystem::path home(Environment && env = current()) +inline filesystem::path home(Environment && env = current()) { - auto find_key = [&](key_view ky) -> value + auto find_key = [&](key_view ky) -> filesystem::path { const auto itr = std::find_if(std::begin(env), std::end(env), - [&](key_value_pair vp) + [&](decltype(*std::begin(env)) vp) { - auto tmp = vp.key() == ky; + auto tmp = std::get<0>(vp) == ky; if (tmp) return true; else return false; }); if (itr != nullptr) - return itr->value_view(); + return std::get<1>(*itr); else - return value_view(); + return ""; }; #if defined(ASIO_WINDOWS) return find_key(L"HOMEDRIVE") + find_key(L"HOMEPATH"); #else - return find_key(L"HOME"); + return find_key("HOME"); #endif } +/// Find the executable `name` in an environment-like type. +/** + * @param env The environment to search. Defaults to the current environment of this process + * + * The environment type passed in must be a range with value T that fulfills the following requirements: + * + * For `T value` + * + * - std::get<0>(value) must return a type comparable to `key_view`. + * - std::get<1>(value) must return a type convertible to `value_view`. + * + * + * @return A filesystem::path to the executable or an empty path if it cannot be found. + * + */ template -inline boost::process::v2::filesystem::path find_executable( - boost::process::v2::filesystem::path name, +inline BOOST_PROCESS_V2_NAMESPACE::filesystem::path find_executable( + BOOST_PROCESS_V2_NAMESPACE::filesystem::path name, Environment && env = current()) { auto find_key = [&](key_view ky) -> value_view { const auto itr = std::find_if(std::begin(env), std::end(env), - [&](key_value_pair vp) + [&](decltype(*std::begin(env)) vp) { - auto tmp = vp.key() == ky; + auto tmp = std::get<0>(vp) == ky; if (tmp) return true; else return false; }); if (itr != nullptr) - return (*itr).value(); + return std::get<1>(*itr); else return value_view(); }; @@ -1297,23 +1400,32 @@ inline boost::process::v2::filesystem::path find_executable( auto path = find_key(L"PATH"); auto pathext = find_key(L"PATHEXT"); for (auto pp_view : path) + { + // first check if it has the extension already + BOOST_PROCESS_V2_NAMESPACE::filesystem::path full_nm(name); + BOOST_PROCESS_V2_NAMESPACE::filesystem::path pp(pp_view.begin(), pp_view.end()); + auto p = pp / nm; + error_code ec; + + if (detail::is_executable(p, ec) && !ec) + return p; + for (auto ext : pathext) { - boost::process::v2::filesystem::path nm(name); + ec.clear(); + BOOST_PROCESS_V2_NAMESPACE::filesystem::path nm(name); nm.concat(ext.begin(), ext.end()); - auto p = boost::process::v2::filesystem::path(pp_view.begin(), pp_view.end()) / nm; + auto p = pp / nm; - error_code ec; - bool is_exec = detail::is_executable(p, ec); - if (!ec && is_exec) + if (detail::is_executable(p, ec) && !ec) return p; } + } #else - auto path = find_key("PATH"); - for (auto pp_view : path) + for (auto pp_view : find_key("PATH")) { - auto p = boost::process::v2::filesystem::path(pp_view.begin(), pp_view.end()) / name; + auto p = BOOST_PROCESS_V2_NAMESPACE::filesystem::path(pp_view.begin(), pp_view.end()) / name; error_code ec; bool is_exec = detail::is_executable(p, ec); if (!ec && is_exec) @@ -1323,173 +1435,165 @@ inline boost::process::v2::filesystem::path find_executable( return {}; } - +/// Get an environment variable from the current process. inline value get(const key & k, error_code & ec) { return detail::get(k.c_str(), ec);} +/// Throwing @overload value get(const key & k, error_code & ec) inline value get(const key & k) { error_code ec; auto tmp = detail::get(k.c_str(), ec); - boost::process::v2::detail::throw_error(ec, "environment::get"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::get"); return tmp; } +/// Disambiguating @overload value get(const key & k, error_code & ec) inline value get(basic_cstring_ref> k, error_code & ec) { return detail::get(k, ec); } +/// Disambiguating @overload value get(const key & k) inline value get(basic_cstring_ref> k) { error_code ec; auto tmp = detail::get(k, ec); - boost::process::v2::detail::throw_error(ec, "environment::get"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::get"); return tmp; } +/// Disambiguating @overload value get(const key & k, error_code & ec) inline value get(const char_type * c, error_code & ec) { return detail::get(c, ec);} +/// Disambiguating @overload value get(const key & k) inline value get(const char_type * c) { error_code ec; auto tmp = detail::get(c, ec); - boost::process::v2::detail::throw_error(ec, "environment::get"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::get"); return tmp; } +/// Set an environment variable for the current process. inline void set(const key & k, value_view vw, error_code & ec) { detail::set(k, vw, ec);} +/// Throwing @overload void set(const key & k, value_view vw, error_code & ec) inline void set(const key & k, value_view vw) { error_code ec; detail::set(k, vw, ec); - boost::process::v2::detail::throw_error(ec, "environment::set"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::set"); } +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) inline void set(basic_cstring_ref> k, value_view vw, error_code & ec) { detail::set(k, vw, ec);} +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) inline void set(basic_cstring_ref> k, value_view vw) { error_code ec; detail::set(k, vw, ec); - boost::process::v2::detail::throw_error(ec, "environment::set"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::set"); } +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) inline void set(const char_type * k, value_view vw, error_code & ec) { detail::set(k, vw, ec);} +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) inline void set(const char_type * k, value_view vw) { error_code ec; detail::set(k, vw, ec); - boost::process::v2::detail::throw_error(ec, "environment::set"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::set"); } +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) template::value>::type> inline void set(const key & k, const Char * vw, error_code & ec) { value val{vw}; detail::set(k, val, ec); } +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) template::value>::type> inline void set(const key & k, const Char * vw) { error_code ec; value val{vw}; detail::set(k, val, ec); - boost::process::v2::detail::throw_error(ec, "environment::set"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::set"); } +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) template::value>::type> inline void set(basic_cstring_ref> k, const Char * vw, error_code & ec) { value val{vw}; detail::set(k, val, ec); } + +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) template::value>::type> inline void set(basic_cstring_ref> k, const Char * vw) { error_code ec; value val{vw}; detail::set(k, val, ec); - boost::process::v2::detail::throw_error(ec, "environment::set"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::set"); } - +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) template::value>::type> inline void set(const char_type * k, const Char * vw, error_code & ec) { value val{vw}; detail::set(k, val, ec); } + +/// Disambiguating @overload void set(const key & k, value_view vw, error_code & ec) template::value>::type> inline void set(const char_type * k, const Char * vw) { error_code ec; value val{vw}; detail::set(k, val, ec); - boost::process::v2::detail::throw_error(ec, "environment::set"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::set"); } - +/// Remove an environment variable from the current process. inline void unset(const key & k, error_code & ec) { detail::unset(k, ec);} +/// Throwing @overload void unset(const key & k, error_code & ec) inline void unset(const key & k) { error_code ec; detail::unset(k, ec); - boost::process::v2::detail::throw_error(ec, "environment::unset"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::unset"); } +/// Disambiguating @overload void unset(const key & k, error_code & ec) inline void unset(basic_cstring_ref> k, error_code & ec) { detail::unset(k, ec); } + +/// Disambiguating @overload void unset(const key & k, error_code & ec) inline void unset(basic_cstring_ref> k) { error_code ec; detail::unset(k, ec); - boost::process::v2::detail::throw_error(ec, "environment::unset"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::unset"); } - +/// Disambiguating @overload void unset(const key & k, error_code & ec) inline void unset(const char_type * c, error_code & ec) { detail::unset(c, ec);} + +/// Disambiguating @overload void unset(const key & k, error_code & ec) inline void unset(const char_type * c) { error_code ec; detail::unset(c, ec); - boost::process::v2::detail::throw_error(ec, "environment::unset"); + BOOST_PROCESS_V2_NAMESPACE::detail::throw_error(ec, "environment::unset"); } } -BOOST_PROCESS_V2_END_NAMESPACE - -namespace std -{ - -template<> -struct tuple_size : integral_constant {}; - -template<> -struct tuple_element<0u, boost::process::v2::environment::key_value_pair> {using type = boost::process::v2::environment::key_view;}; - -template<> -struct tuple_element<1u, boost::process::v2::environment::key_value_pair> {using type = boost::process::v2::environment::value_view;}; - -template<> -struct tuple_size : integral_constant {}; - -template<> -struct tuple_element<0u, boost::process::v2::environment::key_value_pair_view> -{ - using type = boost::process::v2::environment::key_view; -}; - -template<> -struct tuple_element<1u, boost::process::v2::environment::key_value_pair_view> -{ - using type = boost::process::v2::environment::value_view; -}; - -} - // sub process environment stuff -BOOST_PROCESS_V2_BEGIN_NAMESPACE #if defined(BOOST_PROCESS_V2_WINDOWS) namespace windows { struct default_launcher ;} @@ -1497,6 +1601,29 @@ namespace windows { struct default_launcher ;} namespace posix { struct default_launcher ;} #endif +/// Initializer for the environment of sub process. +/** + * This will set the environment in a subprocess: + * + * @code {.cpp} + * + * process proc{executor, find_executable("printenv"), {"foo"}, process_environment{"foo=bar"}}; + * @endcode + * + * The environment initializer will persist it's state, so that it can + * be used multiple times. Do however note the the Operating System is + * allowed to modify the internal state. + * + * @code {.cpp} + * auto exe = find_executable("printenv"); + * process_environment env = {"FOO=BAR", "BAR=FOO"}; + * + * process proc1(executor, exe, {"FOO"}, env); + * process proc2(executor, exe, {"BAR"}, env); + * @endcode + * + * + */ struct process_environment { @@ -1595,9 +1722,8 @@ struct process_environment { std::vector env; - using char_type = typename decay()))[0])>::type; - for (basic_string_view arg : args) - env_buffer.push_back(detail::conv_string(arg.data(), arg.size())); + for (auto && arg: std::forward(args)) + env_buffer.emplace_back(arg); for (auto && e : env_buffer) env.push_back(e.c_str()); @@ -1617,7 +1743,7 @@ struct process_environment error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *); - std::vector env_buffer; + std::vector env_buffer; std::vector env; #endif diff --git a/include/boost/process/v2/error.hpp b/include/boost/process/v2/error.hpp index abf52b94c..621f032d3 100644 --- a/include/boost/process/v2/error.hpp +++ b/include/boost/process/v2/error.hpp @@ -30,7 +30,8 @@ extern BOOST_PROCESS_V2_DECL const error_category& get_exit_code_category(); * void run_my_process(filesystem::path pt, error_code & ec) * { * process proc(pt, {}); - * ec.assign(proc.wait(), error::get_exit_code_category()); + * proc.wait(); + * ec.assign(proc.native_exit_code(), error::get_exit_code_category()); * } * * */ diff --git a/include/boost/process/v2/exit_code.hpp b/include/boost/process/v2/exit_code.hpp index 38b8756ff..5af4615b1 100644 --- a/include/boost/process/v2/exit_code.hpp +++ b/include/boost/process/v2/exit_code.hpp @@ -19,6 +19,24 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE +#if defined(GENERATING_DOCUMENTATION) + +/// The native exit-code type, usually an integral value +/** The OS may have a value different from `int` to represent + * the exit codes of subprocesses. It might also + * contain additional information. + */ +typedef implementation_defined native_exit_code_type; + + +/// Check if the native exit code indicates the process is still running +bool process_is_running(native_exit_code_type code); + +/// Obtain the portable part of the exit code, i.e. what the subprocess has returned from main. +int evaluate_exit_code(native_exit_code_type code); + + +#else #if defined(BOOST_PROCESS_V2_WINDOWS) @@ -65,6 +83,8 @@ inline int evaluate_exit_code(int code) #endif +#endif + BOOST_PROCESS_V2_END_NAMESPACE #endif //BOOST_PROCESS_V2_EXIT_CODE_HPP \ No newline at end of file diff --git a/include/boost/process/v2/impl/error.ipp b/include/boost/process/v2/impl/error.ipp index 1df8d92a5..a8fa068d2 100644 --- a/include/boost/process/v2/impl/error.ipp +++ b/include/boost/process/v2/impl/error.ipp @@ -177,7 +177,7 @@ struct exit_code_category final : public error_category # if defined(SIGWINCH) case SIGWINCH: return "SIGWINCH: Window resize signal (4.3BSD, Sun)"; # endif - default: "Unknown signal"; + default: return "Unknown signal"; } #endif return "exited with other error"; diff --git a/include/boost/process/v2/pid.hpp b/include/boost/process/v2/pid.hpp index 5213fe0ce..48a5e143f 100644 --- a/include/boost/process/v2/pid.hpp +++ b/include/boost/process/v2/pid.hpp @@ -9,6 +9,14 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE +#if defined(GENERATING_DOCUMENTATION) + +//An integral type representing a process id. +typedef implementation_defined pid_type; + + +#else + #if defined(BOOST_PROCESS_V2_WINDOWS) typedef unsigned long pid_type; @@ -17,6 +25,7 @@ typedef unsigned long pid_type; typedef int pid_type; +#endif #endif /// Get the process id of the current process. diff --git a/include/boost/process/v2/popen.hpp b/include/boost/process/v2/popen.hpp index c9c738300..bdc467315 100644 --- a/include/boost/process/v2/popen.hpp +++ b/include/boost/process/v2/popen.hpp @@ -20,7 +20,20 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE - +/// A subprocess with automatically assigned pipes. +/** The purpose os the popen is to provide a convenient way + * to use the stdin & stdout of a process. + * + * @code {.cpp} + * popen proc(executor, find_executable("addr2line"), {argv[0]}); + * asio::write(proc, asio::buffer("main\n")); + * std::string line; + * asio::read_until(proc, asio::dynamic_buffer(line), '\n'); + * @endcode + * + * + * Popen can be used as a stream object in other protocols. + */ template struct basic_popen : basic_process { @@ -35,20 +48,23 @@ struct basic_popen : basic_process typedef basic_popen other; }; + /// Move construct a popen basic_popen(basic_popen &&) = default; + /// Move assign a popen basic_popen& operator=(basic_popen &&) = default; + /// Move construct a popen and change the executor type. template - basic_popen(basic_process&& lhs) + basic_popen(basic_popen&& lhs) : basic_process(std::move(lhs)), stdin_(std::move(lhs.stdin_)), stdout_(std::move(lhs.stdout_)) { } - /// Create an invalid handle + /// Create a closed process handle explicit basic_popen(executor_type exec) : basic_process{std::move(exec)} {} - /// Create an invalid handle + /// Create a closed process handle template explicit basic_popen(ExecutionContext & context, typename std::enable_if< @@ -60,7 +76,7 @@ struct basic_popen : basic_process - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default process launcher. template explicit basic_popen( executor_type executor, @@ -77,7 +93,8 @@ struct basic_popen : basic_process ); } - /// Construct a child from a property list and launch it. + + /// Construct a child from a property list and launch it using the default process launcher. template explicit basic_popen( executor_type executor, @@ -94,7 +111,7 @@ struct basic_popen : basic_process ); } - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default process launcher. template explicit basic_popen( executor_type executor, @@ -110,7 +127,7 @@ struct basic_popen : basic_process ); } - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default process launcher. template explicit basic_popen( ExecutionContext & context, @@ -130,7 +147,7 @@ struct basic_popen : basic_process ); } - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default process launcher. template explicit basic_popen( ExecutionContext & context, @@ -148,13 +165,20 @@ struct basic_popen : basic_process process_stdio{stdin_, stdout_} ); } + + /// The type used for stdin on the parent process side. using stdin_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe; + /// The type used for stdout on the parent process side. using stdout_type = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe; + /// Get the stdin pipe. stdin_type & get_stdin() {return stdin_; } + /// Get the stdout pipe. stdout_type & get_stdout() {return stdout_; } + /// Get the stdin pipe. const stdin_type & get_stdin() const {return stdin_; } + /// Get the stdout pipe. const stdout_type & get_stdout() const {return stdout_; } /// Write some data to the pipe. @@ -169,7 +193,7 @@ struct basic_popen : basic_process * * @throws boost::system::system_error Thrown on failure. An error code of * boost::asio::error::eof indicates that the connection was closed by the - * peer. + * subprocess. * * @note The write_some operation may not transmit all of the data to the * peer. Consider using the @ref write function if you need to ensure that @@ -203,7 +227,7 @@ struct basic_popen : basic_process * @returns The number of bytes written. Returns 0 if an error occurred. * * @note The write_some operation may not transmit all of the data to the - * peer. Consider using the @ref write function if you need to ensure that + * subprocess. Consider using the @ref write function if you need to ensure that * all data is written before the blocking operation completes. */ template @@ -248,7 +272,7 @@ struct basic_popen : basic_process * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code - * pipe.async_write_some(boost::asio::buffer(data, size), handler); + * popen.async_write_some(boost::asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or @@ -389,6 +413,7 @@ struct basic_popen : basic_process stdout_type stdout_{basic_process::get_executor()}; }; +/// A popen object with the default executor. using popen = basic_popen<>; BOOST_PROCESS_V2_END_NAMESPACE diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index 9eb3912d2..4f79a8bb7 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -19,13 +19,18 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) #include +#include #else #include +#include #endif BOOST_PROCESS_V2_BEGIN_NAMESPACE - +/// A class managing a subprocess +/* A `basic_process` object manages a subprocess; it tracks the status and exit-code, + * and will terminate the process on destruction if `detach` was not called. +*/ template struct basic_process { @@ -61,39 +66,23 @@ struct basic_process basic_process(const basic_process&) = delete; basic_process& operator=(const basic_process&) = delete; - basic_process(basic_process&& lhs) - : process_handle_(std::move(lhs.process_handle_)), - attached_(lhs.attached_), - terminated_(lhs.terminated_), - exit_status_{lhs.exit_status_} + /// Move construct the process. It will be detached from `lhs`. + basic_process(basic_process&& lhs) = default; + /// Move assign a process. It will be detached from `lhs`. + basic_process& operator=(basic_process&& lhs) = default; + + /// Move construct and rebind the executor. + template + basic_process(basic_process&& lhs) + : process_handle_(std::move(lhs.process_handle_)), + exit_status_{lhs.exit_status_} - { - lhs.attached_ = false; - } - basic_process& operator=(basic_process&& lhs) { - attached_ = lhs.attached_; - terminated_ = lhs.terminated_; - exit_status_ = lhs.exit_status_; - process_handle_ = std::move(lhs.process_handle_); - lhs.attached_ = false; - return *this; } - template - basic_process(basic_process&& lhs) - : process_handle_(std::move(lhs.process_handle_)), - attached_(lhs.attached_), - terminated_(lhs.terminated_), - exit_status_{lhs.exit_status_} - - - { - lhs.attached_ = false; - } - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default launcher.. template explicit basic_process( executor_type executor, @@ -103,7 +92,7 @@ struct basic_process : basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward(inits)...)) { } - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default launcher.. template explicit basic_process( executor_type executor, @@ -114,7 +103,7 @@ struct basic_process { } - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default launcher.. template explicit basic_process( executor_type executor, @@ -125,7 +114,7 @@ struct basic_process { } - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default launcher.. template explicit basic_process( ExecutionContext & context, @@ -139,7 +128,7 @@ struct basic_process exe, args, std::forward(inits)...)) { } - /// Construct a child from a property list and launch it. + /// Construct a child from a property list and launch it using the default launcher. template explicit basic_process( ExecutionContext & context, @@ -189,13 +178,14 @@ struct basic_process - // tbd behavior + /// Destruct the handle and terminate the process if it wasn't detached. ~basic_process() { - if (attached_ && !terminated_) - process_handle_.terminate_if_running(); + process_handle_.terminate_if_running(); } + /// Sends the process a signal to ask for an interrupt, which the process may interpret as a shutdown. + /** Maybe be ignored by the subprocess. */ void interrupt() { error_code ec; @@ -204,11 +194,13 @@ struct basic_process throw system_error(ec, "interrupt failed"); } + /// Throwing @overload void interrupt() void interrupt(error_code & ec) { process_handle_.interrupt(ec); } + /// Throwing @overload void request_exit(error_code & ec) void request_exit() { error_code ec; @@ -216,11 +208,13 @@ struct basic_process if (ec) throw system_error(ec, "request_exit failed"); } + /// Sends the process a signal to ask for a graceful shutdown. Maybe be ignored by the subprocess. void request_exit(error_code & ec) { process_handle_.request_exit(ec); } + /// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec) void terminate() { error_code ec; @@ -228,11 +222,13 @@ struct basic_process if (ec) detail::throw_error(ec, "terminate failed"); } + /// Unconditionally terminates the process and stores the exit code in exit_status. void terminate(error_code & ec) { process_handle_.terminate(exit_status_, ec); } + /// Throwing @overload wait(error_code & ec) int wait() { error_code ec; @@ -242,6 +238,7 @@ struct basic_process detail::throw_error(ec, "wait failed"); return exit_code(); } + /// Waits for the process to exit, store the exit code internall and return it. int wait(error_code & ec) { if (running(ec)) @@ -249,26 +246,35 @@ struct basic_process return exit_code(); } - void detach() + /// Detach the process. + handle_type detach() { - attached_ = false; +#if defined(BOOST_PROCESS_V2_STANDALONE) + return std::exchange(process_handle_, get_executor()); +#else + return boost::exchange(process_handle_, get_executor()); +#endif } - void join() {wait();} - bool joinable() {return attached_ && process_handle_.is_open(); } - + // Get the native native_handle_type native_handle() {return process_handle_.native_handle(); } int exit_code() const { return evaluate_exit_code(exit_status_); } + /// Get the id of the process; pid_type id() const {return process_handle_.id();} + /// The native handle of the process. + /** This might be undefined on posix systems that only support signals */ native_exit_code_type native_exit_code() const { return exit_status_; } - + /// Checks if the current process is running. + /** If it has already completed the exit code will be stored internally + * and can be obtained by calling `exit_code. + */ bool running() { error_code ec; @@ -281,6 +287,7 @@ struct basic_process return r; } + /// Throwing @overload bool running(error_code & ec) bool running(error_code & ec) noexcept { native_exit_code_type exit_code ; @@ -289,11 +296,12 @@ struct basic_process exit_status_ = exit_code; return r; } + /// Check if the process is referring to an existing process. + /** Note that this might be a process that already exited.*/ bool is_open() const { return process_handle_.is_open(); } - explicit operator bool() const {return is_open(); } - + /// Asynchronously wait for the process to exit and deliver the portable exit-code in the completion handler. template BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, int)) @@ -308,8 +316,6 @@ struct basic_process friend struct basic_process; basic_process_handle process_handle_; - bool attached_{true}; - bool terminated_{false}; native_exit_code_type exit_status_{detail::still_active}; @@ -339,7 +345,7 @@ struct basic_process }; }; - +/// Process with the default executor. typedef basic_process<> process; BOOST_PROCESS_V2_END_NAMESPACE diff --git a/include/boost/process/v2/process_handle.hpp b/include/boost/process/v2/process_handle.hpp index 6080a4db7..04ea6e62c 100644 --- a/include/boost/process/v2/process_handle.hpp +++ b/include/boost/process/v2/process_handle.hpp @@ -23,6 +23,114 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(GENERATING_DOCUMENTATION) +/** A process handle is an unmanaged version of a process. + * This means it does not terminate the proces on destruction and + * will not keep track of the exit-code. + * + * Note that the exit code might be discovered early, during a call to `running`. + * Thus it can only be discovered that process has exited already. + */ +template +struct basic_process_handle +{ + /// The native handle of the process. + /** This might be undefined on posix systems that only support signals */ + using native_handle_type = implementation_defined; + + /// The executor_type of the process_handle + using executor_type = Executor; + + /// Getter for the executor + executor_type get_executor(); + + /// Rebinds the process_handle to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_process_handle other; + }; + + + /// Construct a basic_process_handle from an execution_context. + /** + * @tparam ExecutionContext The context must fulfill the asio::execution_context requirements + */ + template + basic_process_handle(ExecutionContext &context); + + /// Construct an empty process_handle from an executor. + basic_process_handle(executor_type executor); + + /// Construct an empty process_handle from an executor and bind it to a pid. + /** On NON-linux posix systems this call is not able to obtain a file-descriptor and will thus + * rely on signals. + */ + basic_process_handle(executor_type executor, pid_type pid); + + /// Construct an empty process_handle from an executor and bind it to a pid and the native-handle + /** On some non-linux posix systems this overload is not present. + */ + basic_process_handle(executor_type executor, pid_type pid, native_handle_type process_handle); + + /// Move construct and rebind the executor. + template + basic_process_handle(basic_process_handle &&handle); + + /// Get the id of the process + pid_type id() const + { return pid_; } + + /// Terminate the process if it's still running and ignore the result + void terminate_if_running(error_code &); + + /// Throwing @overload void terminate_if_running(error_code & ec; + void terminate_if_running(); + /// wait for the process to exit and store the exit code in exit_status. + void wait(native_exit_code_type &exit_status, error_code &ec); + /// Throwing @overload wait(native_exit_code_type &exit_code, error_code & ec) + void wait(native_exit_code_type &exit_status); + + /// Sends the process a signal to ask for an interrupt, which the process may interpret as a shutdown. + /** Maybe be ignored by the subprocess. */ + void interrupt(error_code &ec); + + /// Throwing @overload void interrupt() + void interrupt(); + + /// Sends the process a signal to ask for a graceful shutdown. Maybe be ignored by the subprocess. + void request_exit(error_code &ec); + + /// Throwing @overload void request_exit(error_code & ec) + 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);\ + /// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec) + 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`. + */ + bool running(native_exit_code_type &exit_code, error_code &ec); + /// Throwing @overload bool running(native_exit_code_type &exit_code, error_code & ec) + bool running(native_exit_code_type &exit_code); + + /// Check if the process handle is referring to an existing process. + bool is_open() const; + + /// Asynchronously wait for the process to exit and deliver the native exit-code in the completion handler. + template + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) + async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)); + +}; + + +#else #if defined(BOOST_PROCESS_V2_WINDOWS) template using basic_process_handle = detail::basic_process_handle_win; @@ -39,8 +147,10 @@ using basic_process_handle = detail::basic_process_handle_fd_or_signal template using basic_process_handle = detail::basic_process_handle_signal; +#endif #endif +/// Process handle with the default executor. using process_handle = basic_process_handle<>; #endif diff --git a/include/boost/process/v2/start_dir.hpp b/include/boost/process/v2/start_dir.hpp index 950089bd1..1410d7a2e 100644 --- a/include/boost/process/v2/start_dir.hpp +++ b/include/boost/process/v2/start_dir.hpp @@ -14,6 +14,8 @@ #include BOOST_PROCESS_V2_BEGIN_NAMESPACE + +/// Initializer for the starting directory of a subprocess to be launched. struct process_start_dir { filesystem::path start_dir; diff --git a/include/boost/process/v2/stdio.hpp b/include/boost/process/v2/stdio.hpp index fa6a7966d..fd43eacfd 100644 --- a/include/boost/process/v2/stdio.hpp +++ b/include/boost/process/v2/stdio.hpp @@ -220,6 +220,46 @@ typedef process_io_binding process_error_binding; } + +/// The initializer for the stdio of a subprocess +/** The subprocess initializer has three members: + * + * - in for stdin + * - out for stdout + * - err for stderr + * + * If the initializer is present all three will be set for the subprocess. + * By default they will inherit the stdio handles from the parent process. + * This means that this will forward stdio to the subprocess: + * + * @code {.cpp} + * asio::io_context ctx; + * v2::process proc(ctx, "/bin/bash", {}, v2::process_stdio{}); + * @endcode + * + * No constructors are provided in order to support designated initializers + * in later version of C++. + * + * * @code {.cpp} + * asio::io_context ctx; + * /// C++17 + * v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.stderr=nullptr}); + * /// C++11 & C++14 + * v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{ {}, {}, nullptr}); + * stdin ^ ^ stderr + * @endcode + * + * Valid initializers for any stdio are: + * + * - `std::nullptr_t` assigning a null-device + * - `FILE*` any open file, including `stdin`, `stdout` and `stderr` + * - a filesystem::path, which will open a readable or writable depending on the direction of the stream + * - `native_handle` any native file handle (`HANDLE` on windows) or file descriptor (`int` on posix) + * - any io-object with a .native_handle() function that is comptaiblie with the above. E.g. a asio::ip::tcp::socket + * - an asio::basic_writeable_pipe for stdin or asio::basic_readable_pipe for stderr/stdout. + * + * + */ struct process_stdio { detail::process_input_binding in; diff --git a/test/v2/cstring_ref.cpp b/test/v2/cstring_ref.cpp index 3f17841c6..f2b82babc 100644 --- a/test/v2/cstring_ref.cpp +++ b/test/v2/cstring_ref.cpp @@ -53,8 +53,6 @@ BOOST_AUTO_TEST_CASE(cstring_view_test) BOOST_CHECK_EQUAL(sv.length(), 5); BOOST_CHECK_EQUAL(sv.c_str(), s.c_str() + 1); - auto cp = sv; - BOOST_CHECK_EQUAL(sv.substr(2).c_str(), s.c_str() + 3); bp2::string_view ssv = sv; diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index 16e4a6280..0b56982a8 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -14,6 +14,8 @@ #include +#include + namespace bp2 = boost::process::v2; namespace bpe = boost::process::v2::environment; @@ -57,7 +59,7 @@ BOOST_AUTO_TEST_CASE(environment) ec.clear(); for (auto && ke : bpe::current()) - BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); + BOOST_CHECK_EQUAL(bpe::get(std::get<0>(ke)), std::get<1>(ke)); #if defined(BOOST_PROCESS_V2_POSIX) @@ -71,6 +73,28 @@ BOOST_AUTO_TEST_CASE(environment) BOOST_CHECK_EQUAL(bpe::key_value_pair("FOO", {"X", "YY", "Z42"}), cmp); #endif + +#if defined(BOOST_PROCESS_V2_POSIX) + std::unordered_map custom_env = + { + {"HOME", "/home/byzantium"}, + {"HOMEDRIVE", "X:"}, + {"HOMEPATH", "\\users\\theodora"} + }; + BOOST_CHECK_EQUAL(bpe::home(custom_env), "/home/byzantium"); +#else + std::unordered_map custom_env = + { + {"HOME", L"/home/byzantium"}, + {"HOMEDRIVE", L"X:"}, + {"HOMEPATH", L"\\users\\theodora"} + }; + BOOST_CHECK_EQUAL(bpe::home(custom_env), L"X:\\Users\\theodora"); + +#endif + + bp2::process_environment env{custom_env }; + boost::ignore_unused(env); } @@ -105,7 +129,7 @@ BOOST_AUTO_TEST_CASE(wenvironment) BOOST_CHECK(ec); for (const auto ke : bpe::current()) - BOOST_CHECK_EQUAL(bpe::get(ke.get<0>()), ke.get<1>()); + BOOST_CHECK_EQUAL(bpe::get(std::get<0>(ke)), std::get<1>(ke)); #if defined(BOOST_PROCESS_V2_WINDOWS) BOOST_CHECK_EQUAL(bpe::key(L"FOO"), bpe::key_view(L"Foo")); diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 749337744..61e1c7458 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -239,7 +239,7 @@ BOOST_AUTO_TEST_CASE(print_args_err) auto sz = asio::read(rp, st, ec); - BOOST_CHECK_NE(sz , 0); + BOOST_CHECK_NE(sz , 0u); BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); std::string line; @@ -406,7 +406,7 @@ std::string read_env(const char * name, Inits && ... inits) std::string out; bpv::error_code ec; - auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); + asio::read(rp, asio::dynamic_buffer(out), ec); BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); trim_end(out); diff --git a/test/v2/utf8.cpp b/test/v2/utf8.cpp index d4d731cc3..2abc04fff 100755 --- a/test/v2/utf8.cpp +++ b/test/v2/utf8.cpp @@ -15,8 +15,19 @@ BOOST_AUTO_TEST_CASE(test_codecvt) { - auto end = [](const auto * c){return std::char_traits>::length(c);}; - + struct end_t + { + std::size_t operator()(const char * c) + { + return std::char_traits::length(c); + } + std::size_t operator()(const wchar_t * c) + { + return std::char_traits::length(c); + } + } end{}; + + const char * in = "test-input-\320\240\320\230\320\221\320\220"; const wchar_t * win_t = L"test-input-\u0420\u0418\u0411\u0410"; From 25669a78def29d21107b5b48f1e280295429b2af Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 4 Jun 2022 16:59:40 +0800 Subject: [PATCH 073/397] Removed superfluous builds --- .drone.star | 48 ++++++++++++------------------------------------ 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/.drone.star b/.drone.star index 81d020ad3..dca2967c9 100644 --- a/.drone.star +++ b/.drone.star @@ -14,47 +14,23 @@ windowsglobalimage="cppalliance/dronevs2019" def main(ctx): return [ - #freebsd_cxx("FreeBSD", "g++10", packages="g++10", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), - linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), + #freebsd_cxx("FreeBSD", "g++10", packages="g++10", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), + linux_cxx("docs", "", packages="doc book docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv), + linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), + linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), + linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), + linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb mlocate", + image="cppalliance/droneubuntu2004:1", buildtype="boost_v1", buildscript="drone", environment={"GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "11", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), # A set of jobs based on the earlier .travis.yml configuration: - linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb mlocate", image="cppalliance/droneubuntu2004:1", buildtype="boost_v1", buildscript="drone", environment={"GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "14", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), - linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={ "B2_TOOLSET": "clang-7", "B2_CXXSTD": "17,2a", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "11", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), - linux_cxx("GCC Valgrind", "g++", packages="g++-7 libssl-dev valgrind mlocate", image="cppalliance/droneubuntu2004:1", buildtype="boost_v1", buildscript="drone", environment={ "VARIANT": "process_valgrind", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), - linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), - linux_cxx("GCC 8, C++17, libstdc++, release", "g++-8", packages="g++-8 mlocate", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++-8", "CXXSTD" : "17" }, globalenv=globalenv), + linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "B2_TOOLSET": "clang-7", "B2_CXXSTD": "11", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "11", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), + linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={"VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "11", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), - linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv), - # Next, a standard list of tests from boost-ci: - linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), - # linux_cxx("tsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'tsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_TSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_JOB_UUID': 'da4b9237ba'}, globalenv=globalenv), - linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11,14', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), - # a boost-ci based version of codecov. However, codecov has already been run, above. - # linux_cxx("codecov", "g++-8", packages="g++-8", buildtype="codecov", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'codecov.io', 'B2_CXXSTD': '11', 'B2_TOOLSET': 'gcc-8', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_JOB_UUID': 'b6589fc6ab', "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv), - # gcc 4.8 is failing: - # # linux_cxx("gcc 4.8", "g++-4.8", packages="g++-4.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={'B2_TOOLSET': 'gcc-4.8', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': 'ac3478d69a'}, globalenv=globalenv), linux_cxx("gcc 5", "g++-5", packages="g++-5", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-5', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), - linux_cxx("gcc 6", "g++-6", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-6', 'B2_CXXSTD': '11,14', 'DRONE_JOB_UUID': 'fe5dbbcea5'}, globalenv=globalenv), - # # linux_cxx("gcc 7", "g++-7", packages="g++-7", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-7', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': '0ade7c2cf9'}, globalenv=globalenv), - # # linux_cxx("gcc 8", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': 'b1d5781111'}, globalenv=globalenv), - # # linux_cxx("gcc 9", "g++-9", packages="g++-9", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-9', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079149'}, globalenv=globalenv), - # # linux_cxx("gcc 10", "g++-10", packages="g++-10", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'gcc-10', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079159'}, globalenv=globalenv), - linux_cxx("gcc 11", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '17ba079169'}, globalenv=globalenv), linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), - # # linux_cxx("clang 4.0", "clang++-4.0", packages="clang-4.0 libstdc++-6-dev", llvm_os="xenial", llvm_ver="4.0", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-4.0', 'B2_CXXSTD': '11,14', 'DRONE_JOB_UUID': 'bd307a3ec3'}, globalenv=globalenv), - # # linux_cxx("clang 5.0", "clang++-5.0", packages="clang-5.0 libstdc++-7-dev", llvm_os="bionic", llvm_ver="5.0", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-5.0', 'B2_CXXSTD': '11,14', 'DRONE_JOB_UUID': 'fa35e19212'}, globalenv=globalenv), - # # linux_cxx("clang 6.0", "clang++-6.0", packages="clang-6.0 libc6-dbg libc++-dev libstdc++-8-dev", llvm_os="bionic", llvm_ver="6.0", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-6.0', 'B2_CXXSTD': '14,17', 'DRONE_JOB_UUID': 'f1abd67035'}, globalenv=globalenv), - # # linux_cxx("clang 7", "clang++-7", packages="clang-7 libc6-dbg libc++-dev libstdc++-8-dev", llvm_os="bionic", llvm_ver="7", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-7', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '1574bddb75'}, globalenv=globalenv), - # # linux_cxx("clang 8", "clang++-8", packages="clang-8 libc6-dbg libc++-dev libstdc++-8-dev", llvm_os="bionic", llvm_ver="8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-8', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '0716d9708d'}, globalenv=globalenv), - # # linux_cxx("clang 9", "clang++-9", packages="clang-9 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="bionic", llvm_ver="9", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-9', 'B2_CXXSTD': '11,14,17,2a', 'DRONE_JOB_UUID': '9e6a55b6b4'}, globalenv=globalenv), - # # linux_cxx("clang 10", "clang++-10", packages="clang-10 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="focal", llvm_ver="10", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-10', 'B2_CXXSTD': '11,14,17,2a', 'DRONE_JOB_UUID': '9e6a55b6c4'}, globalenv=globalenv), - # # linux_cxx("clang 11", "clang++-11", packages="clang-11 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="focal", llvm_ver="11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-11', 'B2_CXXSTD': '11,14,17,2a', 'DRONE_JOB_UUID': '9e6a55b6b4'}, globalenv=globalenv), - linux_cxx("clang 12", "clang++-12", packages="clang-12 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="focal", llvm_ver="12", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-12', 'B2_CXXSTD': '11,14,17,20', 'DRONE_JOB_UUID': '9e6a55b6b5'}, globalenv=globalenv), - # # linux_cxx("clang 6.0 libc++", "clang++-6.0", packages="clang-6.0 libc6-dbg libc++-dev libstdc++-8-dev libc++abi-dev", llvm_os="bionic", llvm_ver="6.0", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-6.0', 'B2_CXXSTD': '11,14', 'B2_STDLIB': 'libc++', 'DRONE_JOB_UUID': 'b3f0c7f6bb'}, globalenv=globalenv), - osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={'B2_TOOLSET': 'clang', 'B2_CXXSTD': '11,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), + osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={'B2_TOOLSET': 'clang', 'B2_CXXSTD': '11,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), - windows_cxx("msvc-14.1", "", image="cppalliance/dronevs2017", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "msvc-14.1", "CXXSTD": "17", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), - # # windows_cxx("msvc-14.2", "", image="cppalliance/dronevs2019", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "msvc-14.2", "CXXSTD": "17", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), - windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "20", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), + windows_cxx("msvc-14.1", "", image="cppalliance/dronevs2017", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "msvc-14.1", "CXXSTD": "11", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), + windows_cxx("msvc-14.3", "", image="cppalliance/dronevs2022:1", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "msvc-14.3", "CXXSTD": "11", "DEFINE" : "BOOST_BEAST_USE_STD_STRING_VIEW", "ADDRESS_MODEL": "64"}), ] # from https://github.com/boostorg/boost-ci From 4e64224ef10a247db91d72badbbce4150ebaed47 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 4 Jun 2022 17:21:28 +0800 Subject: [PATCH 074/397] Starlark fix & added alias for subdirector --- .drone.star | 6 +++--- test/Jamfile.jam | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.drone.star b/.drone.star index dca2967c9..a62e69482 100644 --- a/.drone.star +++ b/.drone.star @@ -15,16 +15,16 @@ windowsglobalimage="cppalliance/dronevs2019" def main(ctx): return [ #freebsd_cxx("FreeBSD", "g++10", packages="g++10", buildtype="boost", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), - linux_cxx("docs", "", packages="doc book docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv), + linux_cxx("docs", "", packages="docbook docbook-xml docbook-xsl xsltproc libsaxonhe-java default-jre-headless flex libfl-dev bison unzip rsync mlocate", image="cppalliance/droneubuntu1804:1", buildtype="docs", buildscript="drone", environment={"COMMENT": "docs"}, globalenv=globalenv), linux_cxx("asan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv, privileged=True), linux_cxx("ubsan", "g++-8", packages="g++-8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-8', 'B2_CXXSTD': '11', 'B2_UBSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv), linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={ 'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '17ba079169m'}, arch="arm64", globalenv=globalenv), linux_cxx("GCC 10, Debug + Coverage", "g++-10", packages="g++-10 libssl-dev libffi-dev binutils-gold gdb mlocate", - image="cppalliance/droneubuntu2004:1", buildtype="boost_v1", buildscript="drone", environment={"GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "11", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), + image="cppalliance/droneubuntu2004:1", buildtype="boost", buildscript="drone", environment={"GCOV": "gcov-10", "LCOV_VERSION": "1.15", "VARIANT": "process_coverage", "TOOLSET": "gcc", "COMPILER": "g++-10", "CXXSTD": "11", "DRONE_BEFORE_INSTALL" : "process_coverage", "CODECOV_TOKEN": {"from_secret": "codecov_token"}}, globalenv=globalenv, privileged=True), # A set of jobs based on the earlier .travis.yml configuration: linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "B2_TOOLSET": "clang-7", "B2_CXXSTD": "11", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "11", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), - linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost_v1", buildscript="drone", environment={"VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "11", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), + linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost", buildscript="drone", environment={"VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "11", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), linux_cxx("gcc 5", "g++-5", packages="g++-5", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-5', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={'B2_TOOLSET': 'clang', 'B2_CXXSTD': '11,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 2563d9052..7d2c3db8d 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -132,3 +132,4 @@ test-suite without-valgrind : ; +alias v2-tests : v2//standalone v2//with_target ; \ No newline at end of file From 3163496b70133a3de8341ae68e17a7946875e411 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 4 Jun 2022 21:19:40 +0800 Subject: [PATCH 075/397] Added documentation --- doc/Jamfile.jam | 41 +++++- doc/process.qbk | 3 + doc/v2.qbk | 11 ++ doc/v2/env.qbk | 48 +++++++ doc/v2/introduction.qbk | 84 +++++++++++ doc/v2/launcher.qbk | 126 +++++++++++++++++ doc/v2/quickstart.qbk | 124 ++++++++++++++++ doc/v2/start_dir.qbk | 16 +++ doc/v2/stdio.qbk | 89 ++++++++++++ include/boost/process/v2/default_launcher.hpp | 2 +- include/boost/process/v2/environment.hpp | 132 ++++++++++++------ include/boost/process/v2/execute.hpp | 1 + include/boost/process/v2/popen.hpp | 4 +- test/v2/environment.cpp | 24 +++- 14 files changed, 659 insertions(+), 46 deletions(-) create mode 100644 doc/v2.qbk create mode 100644 doc/v2/env.qbk create mode 100644 doc/v2/introduction.qbk create mode 100644 doc/v2/launcher.qbk create mode 100644 doc/v2/quickstart.qbk create mode 100644 doc/v2/start_dir.qbk create mode 100644 doc/v2/stdio.qbk diff --git a/doc/Jamfile.jam b/doc/Jamfile.jam index f1d506bbc..15ff339dd 100644 --- a/doc/Jamfile.jam +++ b/doc/Jamfile.jam @@ -30,7 +30,7 @@ doxygen autodoc : $(INCLUDES)/boost/process.hpp [ glob $(INCLUDES)/boost/process/*.hpp ] -: + : EXCLUDE_SYMBOLS=BOOST_ASIO_INITFN_RESULT_TYPE PREDEFINED=BOOST_PROCESS_DOXYGEN HIDE_UNDOC_CLASSES=YES @@ -42,11 +42,50 @@ doxygen autodoc ; + +doxygen reference_v2 +: + $(INCLUDES)/boost/process/v2.hpp + [ glob $(INCLUDES)/boost/process/v2/*.hpp ] + : + EXCLUDE_SYMBOLS=BOOST_ASIO_INITFN_RESULT_TYPE + PROJECT_NAME="Process V2" + PROJECT_BRIEF="The process library" + MACRO_EXPANSION=YES + EXPAND_ONLY_PREDEF=YES + "PREDEFINED=\\ + GENERATING_DOCUMENTATION=1 \\ + BOOST_PROCESS_V2_ASIO_NAMESPACE=boost::asio \\ + \"BOOST_PROCESS_V2_BEGIN_NAMESPACE=namespace boost { namespace process { namespace v2 { \" \\ + \"BOOST_PROCESS_V2_END_NAMESPACE= } } }\" \\ + BOOST_PROCESS_V2_NAMESPACE=boost::process::v2 \\ + BOOST_PROCESS_V2_DECL \\ + BOOST_PROCESS_V2_SOURCE \\ + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(x,y)=deduced \\ + BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(X)=Token \\ + BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(E)=DEFAULT_TYPE \\ + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN=DEFAULT \\ + BOOST_CONSTEXPR=constexpr \\ + BOOST_CXX14_CONSTEXPR=constexpr \\ + BOOST_ATTRIBUTE_NODISCARD=[[nodiscard]] + " + reference_v2 + SHOW_USED_FILES=NO + SHOW_FILES=NO + SHOW_NAMESPACES=YES + CLASS_DIAGRAMS=NO + SORT_MEMBERS_CTORS_1ST=YES + HIDE_UNDOC_CLASSES=NO + . +; + + boostbook standalone : process.qbk : autodoc + reference_v2 images images_glob boost.root=../../../.. diff --git a/doc/process.qbk b/doc/process.qbk index d7a158d9e..6dbfa57ad 100644 --- a/doc/process.qbk +++ b/doc/process.qbk @@ -11,6 +11,8 @@ ] ] +[note [link process.v2 Process V2] is available as experimental] + [include introduction.qbk] [include concepts.qbk] [include tutorial.qbk] @@ -19,3 +21,4 @@ [include faq.qbk] [xinclude autodoc.xml] [include acknowledgements.qbk] +[include v2.qbk] \ No newline at end of file diff --git a/doc/v2.qbk b/doc/v2.qbk new file mode 100644 index 000000000..3b6bb08ef --- /dev/null +++ b/doc/v2.qbk @@ -0,0 +1,11 @@ +[section:v2 Process V2] + +[include v2/introduction.qbk] +[include v2/quickstart.qbk] +[include v2/launcher.qbk] +[include v2/start_dir.qbk] +[include v2/stdio.qbk] +[include v2/env.qbk] +[xinclude reference_v2.xml] + +[endsect] diff --git a/doc/v2/env.qbk b/doc/v2/env.qbk new file mode 100644 index 000000000..3f44dc2ab --- /dev/null +++ b/doc/v2/env.qbk @@ -0,0 +1,48 @@ +[section:env Environment] + +The `environment` namespace provides all sorts of facilities to query and manipulate the environment of the current process. + +The api should be straight forward, but one oddity that needs to be pointed out is, that environment names +are not case sensitive on windows. The key_traits class implements the proper traits depending on the current system. + +Additionally, environment can be lists separated by `:` or `;`; `environment::value` and +`environment::value_view` can be used to iterate those. + +Beyond that, the requirements on an environment are a low as possible; +an environment is either a list of strings or a list of string-pairs. It is however recommented to use the environment types, +as to have the right value comparisons. + +To note is the `find_executable` functions, which searches in an environment for an executable. + +``` + // search in the current environment + auto exe = environment::find_executable("g++"); + + std::unordered_map my_env = + { + {"SECRET", "THIS_IS_A_TEST"} + {"PATH", {"/bin", "/usr/bin"} + }; + + auto other_exe = environment::find_executable("g++", my_env); +``` + +[section:process_env Subprocess environment] + +The subprocess environment assignment follows the same constraints: + +``` + asio::io_context ctx; + std::unordered_map my_env = + { + {"SECRET", "THIS_IS_A_TEST"} + {"PATH", {"/bin", "/usr/bin"} + }; + auto exe = find_executable("g++"), my_env); + process proc(ctx, exe, {"main.cpp"}, process_environment(my_env)); + process pro2(ctx, exe, {"test.cpp"}, process_environment(my_env)); +``` + +[endsect] + +[endsect] \ No newline at end of file diff --git a/doc/v2/introduction.qbk b/doc/v2/introduction.qbk new file mode 100644 index 000000000..05ffca0d1 --- /dev/null +++ b/doc/v2/introduction.qbk @@ -0,0 +1,84 @@ +[section:introduction Introduction] + +Boost.process V2 is an redesign of boost.process, based on previous +design mistakes & improved system APIs. + +The major changes are + +* Simplified interface +* Reliance on pidfd_open on linux +* Full asio integration +* Removed unreliable functionality +* UTF8 Support +* separate compilation + +[section:simplified Simplified Interface] + +In process v1 one can define partial settings in the constructor of the process, +which has lead to a small DSL. + + child c{exe="test", args+="--help", std_in < null(), env["FOO"] += "BAR"}; + +While this looks fancy at first, it really does not scale well with more parameters. +For process v2, the interfaces is simple: + + extern std::unordered_map my_env; + extern asio::io_context ctx; + process proc(ctx, "./test", {"--help"}, process_io{nullptr, {}, {}}, process_environment(my_env)); + +Every initializer adresses one logical compoent (e.g. stdio) instead of multiple ones accumulating. +Furthermore, every process has a path and arguments, instead of a confusing mixture of cmd-style and +exe-args that can be randomly spread out. + +[endsect] + +[section:pidfd_open pidfd_open] + +Since process v1 came out, linux has moved along and added pidfd_open which allows users to get a +file descriptor for a process. This is much more reliable since it is not as easy to miss as a `SIGCHLD`. +FreeBSD has a similar feature with `pdfork` which is also supported, while windows has provided `HANDLE`s +for processes all along. +Unless the OS doesn't support it, process v2 will use file descriptors and handles to implement waiting +for processes. + +[endsect] + +[section:asio Full asio integration] + +Process v1 aimed to make asio optional, but synchronous IO with subprocesses usually means one is begging +for deadlocks. +Since asio added pipes in boost 1.78, boost process V2 is fully asio based and uses it's pipes and +file-handles for the subprocess. + +[endsect] + +[section:unreliable Unreliable functionality] + +Certain parts of boost.process were not as reliable as they should've been. + +This is true of `limit_handles` which cannot be done safely on posix (it only finds a subset of FDs), +and especially for the `wait_for`` and `wait_until` functions on the process. +The latter are easy to do on windows, but posix does not provide an API for this. +Thus the wait_for used signals or fork, which was all but safe. +Since process v2 is based on asio and thus supports cancellation, +a wait_for can not safely be implemented with an async_wait + timeout. + +[endsect] + +[section:utf8 UTF-8] + +["UTF-8 or GTFO]--Vinnie Falco + +Instead of using ascii-APIs on windows, process V2 just assumes UTF-8 everywhere. + +[endsect] + +[section:src Separate compilation] + +Boost.process v2 supports separate compilation similar to other boost libraries. +It can be achieved by defining `BOOST_PROCESS_V2_SEPARATE_COMPILATION` and including +`` in a single compile unit. + +[endsect] + +[endsect] diff --git a/doc/v2/launcher.qbk b/doc/v2/launcher.qbk new file mode 100644 index 000000000..a47734b57 --- /dev/null +++ b/doc/v2/launcher.qbk @@ -0,0 +1,126 @@ +[section:launchers Launcher] + +The process creation is done by a process_launcher. +The constructor of `process` will use the default_launcher, which varies by system. +There are additional launcher available on most systems. + +[table:launchers Launcher overview + [[Name] [Summary] [Default on] [Available on]] + [[`windows::default_launcher`] [Launcher using `CreateProcessW`] [windows] [windows]] + [[`windows::as_user_launcher`] [Launcher using `CreateProcessAsUserW`] [] [windows]] + [[`windows::with_logon_launcher`] [Launcher using `CreateProcessWithLogonW`] [] [windows]] + [[`windows::with_token_launcher`] [Launcher using `CreateProcessWithTokenW`] [] [windows]] + [[`posix::default_launcher`] [Launcher using fork & an error pipe] [most of posix] [posix]] + [[`posix::fork_and_forget`] [Launcher using fork without error pipe] [] [posix]] + [[`posix::pdfork_launcher`] [Launcher using pdfork with an error pipe] [FreeBSD] [FreeBSD]] + [[`posix::vfork_launcher`] [Launcher using vfork] [] [posix]] +] + +A launcher is invoked through the call operator. + +``` + auto l = windows::as_user_launcher((HANDLE)0xDEADBEEF); + asio::io_context ctx; + boost::system::eror_code ec; + auto proc = l(ctx, ec, "C:\\User\\boost\\Downloads\\totally_not_a_virus.exe", {}); +``` + +The launcher will call certain functions on the initializer if they're present, as documented below. +The initializer are used to modify the process behaviour. + +[section:linux Linux Launchers] + +The default and pdfork launchers on linux open an internal pipe to communicate errors that occur after forking back to the parent process. + +This can be prevented by using the `fork_and_forget_launcher`. +Alternatively, the `vfork_launcher` can report errors directly back to the parent process. + +Thus some calls to the initializers occur after forking from the child process. + +``` + struct custom_initalizer + { + // functions called from the parent process: + + + // called before a call to fork. A returned error will cancel the launch. + template + error_code on_setup(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line)); + + // called for every initializer if an error occured during setup or process creation + template + void on_error(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line), + const error_code & ec); + + // called after successful process creation + template + void on_success(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line)); + + // called for every initializer if an error occured when forking, in addtion to on_error. + template + void on_fork_error(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line), + const error_code & ec); + + + // called before a call to execve. A returned error will cancel the launch. Called from the child process. + template + error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line)); + + + // called after a failed call to execve from the child process. + template + void on_exec_error(Launcher & launcher, const filesystem::path &executable, const char * const * (&cmd_line)); + }; +``` + +The call sequence on success: +''' + +''' + +The call sequence when fork fails: +''' + +''' + +The call sequence when exec fails: +''' + +''' + +[endsect] + +[section:windows Windows Launchers] + +Windows launchers are pretty streight forward, they will call the following functions on the initializer if present. + +``` + struct custom_initializer + { + // called before a call to CreateProcess. A returned error will cancel the launch. + template + error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line); + + // called for every initializer if an error occured during setup or process creation + template + void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line, + const error_code & ec); + + // called after successful process creation + template + void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line); + + }; +``` + +[note All the additional launchers for windows inherit `default_launcher`] + +The call sequence is as follows: +''' + +''' + +[endsect] + + +[endsect] \ No newline at end of file diff --git a/doc/v2/quickstart.qbk b/doc/v2/quickstart.qbk new file mode 100644 index 000000000..73ef4338b --- /dev/null +++ b/doc/v2/quickstart.qbk @@ -0,0 +1,124 @@ +[section:quickstart Quickstrat] + +A process needs four things to be launched: + +* an asio execution_context / executor +* a path to an executable +* a list of arguments +* a variadic set of initializers + +``` + // process(asio::any_io_executor, filesystem::path, range args, AdditionalInitializers...) + asio::io_context ctx; + process proc(ctx, "/usr/bin/cp", {"source.txt", "target.txt"}); +``` + + +The started process can then be awaited or terminated. + +[section:lifetime Lifetime] + +If the process handle goes out of scope, it will terminate the subprocess. +You can prevent this, by calling `proc.detach()`; do however note that this +can lead to zombie processes. + +A process that completed will deliver an exit-code, +which can be obtained by calling `.exit_code` on the exited process and which is +also returned from `.wait()`. + +``` + process proc("/bin/ls", {}); + assert(proc.wait() == 0); +``` + +The normal exit-code is what the subprocess returned from `main`; +posix will however add addtional information about the process. +This is called the `native_exit_code`. + + +The `.running()` function can be used to detect if the process is still active. + +[endsect] + +[section:signal Signalling the subprocess] + +The parent process can signal the subprocess demaning certain actions. + +`.terminate` will cause the subprocess to exit immediately (`SIGKILL` on posix). +This is the only reliable & portable way to end a subprocess. + +``` + process proc("/bin/totally-not-a-virus", {}); + proc.terminate(); +``` + +`.request_exit` will ask the subprocess to shutdown (`SIGTERM` on posix), +which the subprocess might ignore. + +``` + process proc("/bin/bash", {}); + proc.request_exit(); + proc.wait(); +``` + +`.interrupt` will send an SIGINT to the subprocess, which a subprocess might +interpret as a signal to shutdown. + +[warning interrupt requires the initializer `windows::create_new_process_group` to be set] + +``` + process proc("/usr/bin/addr2line", {}); + proc.request_exit(); + proc.wait(); +``` + +[endsect] + +[section:execute Execute functions] + +Process v2 provides `execute` and `async_execute` functons that can be used for managed executions. + +``` + assert(execute(process("/bin/ls", {}) == 0)); +``` + +The async version supports cancellation and will forward cancellation types as follows: + +- asio::cancellation_type::total -> interrupt +- asio::cancellation_type::partial -> request_exit +- asio::cancellation_type::terminal -> terminate + +``` + asio::io_context ctx; + asio::steady_timer timout{ctx, std::chrono::seconds(10)}; + + asio::cancellation_signal sig; + async_execute(process("/usr/bin/g++", {"hello_world.cpp"}), + asio::bind_cancellation_slot(sig.slot(), + [&](error_code ec, int exit_code) + { + timeout.cancel(); // we're done earlier + })); + + timeout.async_wait( + [&](error_code ec) + { + if (ec) // we were cancelled, do nothing + return ; + sig.emit(asio::cancellation_type::partial); + // request exit first, but terminate after another 10 sec + timeout.expires_after(std::chrono::seconds(10)); + timeout.async_wait( + [&](error_code ec) + { + if (!ec) + sig.emit(asio::cancellation_type::terminal); + }); + ); + }); + +``` + +[endsect] + +[endsect] \ No newline at end of file diff --git a/doc/v2/start_dir.qbk b/doc/v2/start_dir.qbk new file mode 100644 index 000000000..3751edff9 --- /dev/null +++ b/doc/v2/start_dir.qbk @@ -0,0 +1,16 @@ +[section:start_dir process_start_dir] + +The easier initializer to use is `process_start_dir`: + +``` + asio::io_context ctx; + process ls(ctx, "/ls", {}, process_start_dir("/home")); + ls.wait(); +``` + +This will run `ls` in the folder `/home` instead of the current folder. + +[warning If your path is relative, it may fail on posix, because the directory is changed before a call to execve.] + + +[endsect] \ No newline at end of file diff --git a/doc/v2/stdio.qbk b/doc/v2/stdio.qbk new file mode 100644 index 000000000..09a3b6315 --- /dev/null +++ b/doc/v2/stdio.qbk @@ -0,0 +1,89 @@ +[section:stdio stdio] + +When using io with a subprocess, all three standard streams (stdin, stdout, stderr) get set for the child-process. +The default setting is to inherit the parent process. + +This feature meant to be flexible, which is why there is little checking on the arguments assigned to one of those streams. + +[section:pipe Pipe] + +asio pipes can be used for io. When using in process_stdio they will get +automatically connected and the other side will get assigned to the child process: + +``` + asio::io_context ctx; + asio::readable_pipe rp; + + process proc(ctx, "/usr/bin/g++", {"--version"}, process_stdio{{ /* in to default */}, rp, { /* err to default */ }}); + std::string output; + + system::error_code ec; + rp.read(asio::dynamic_buffer(output), ec); + assert(ec == asio::eof); + proc.wait(); +``` + +readable pipes can be assigned to `out` an `err``, while writable_pipes can be assigned to `in`. + +[endsect] + +[section:file `FILE*`] + +`FILE*` can also be used for either side; this allows the `stdin`, `stderr`, `stdout` macros to be used: + +``` + asio::io_context ctx; + // forward both stderr & stdout to stdout of the parent process + process proc(ctx, "/usr/bin/g++", {"--version"}, process_stdio{{ /* in to default */}, stdout, stdout}); + proc.wait(); +``` + +[endsect] + +[section:null `nullptr`] + +`nullptr` may be used to set a given stream to be opened on the null-device (`/dev/null` on posix, `NUL` on windows). +This is used to ignore output or give only EOF as input. + +``` + asio::io_context ctx; + // ignore stderr + process proc(ctx, "/usr/bin/g++", {"--version"}, process_stdio{{ /* in to default */}, {}, nullptr}); + proc.wait(); +``` + +[endsect] + + +[section:native_handle `native_handle`] + +A native handle can be used as well, which means an `int` on posix or a `HANDLE` on windows. +Furthermore, any object that has a `native_handle` returning that native handle type is valid, too. + + +``` + asio::io_context ctx; + // ignore stderr + asio::ip::tcp::socket sock{ctx}; + connect_my_socket(sock); + process proc(ctx, "~/not-a-virus", {}, process_stdio{sock, sock, nullptr}); + proc.wait(); +``` + +[endsect] + +[section:popen popen] + +Additionally, process v2 provides a `popen` class. +It starts a process and connects pipes for stdin and stdout, so that the popen object can be used as a stream. + +``` + popen proc(executor, "/usr/bin/addr2line, {argv[0]}); + asio::write(proc, asio::buffer("main\n")); + std::string line; + asio::read_until(proc, asio::dynamic_buffer(line), '\n'); +``` + +[endsect] + +[endsect] \ No newline at end of file diff --git a/include/boost/process/v2/default_launcher.hpp b/include/boost/process/v2/default_launcher.hpp index 7a1ac3123..42a26b724 100644 --- a/include/boost/process/v2/default_launcher.hpp +++ b/include/boost/process/v2/default_launcher.hpp @@ -40,7 +40,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE * */ -typedef implementation-typed default_process_launcher; +typedef implementation_defined default_process_launcher; #else #if defined(BOOST_PROCESS_V2_WINDOWS) diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index e7f562630..4252fd79a 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -199,6 +199,7 @@ struct key_view string_view_type value_; }; +/// A view for a value in an environment struct value_view { using value_type = char_type; @@ -293,6 +294,7 @@ struct value_view string_view_type value_; }; +/// A view for a key value pair in an environment struct key_value_pair_view { using value_type = char_type; @@ -478,7 +480,7 @@ inline std::size_t hash_value(const BOOST_PROCESS_V2_NAMESPACE::environment::key return hash ; } - +/// A class representing a key within an environment. struct key { using value_type = char_type; @@ -624,6 +626,7 @@ struct key string_type value_; }; +#if !defined(GENERATING_DOCUMENTATION) template typename std::enable_if< @@ -685,6 +688,18 @@ typename std::enable_if< bool>::type operator >(const T &l, const U & r) { return key_view(l) > key_view(r); } +#else + + +bool operator==(const value_view &, const value_view); +bool operator!=(const value_view &, const value_view); +bool operator<=(const value_view &, const value_view); +bool operator< (const value_view &, const value_view); +bool operator> (const value_view &, const value_view); +bool operator>=(const value_view &, const value_view); + +#endif + struct value { @@ -840,6 +855,7 @@ struct value }; +#if !defined(GENERATING_DOCUMENTATION) template typename std::enable_if< @@ -901,8 +917,16 @@ typename std::enable_if< bool>::type operator >(const T &l, const U & r) { return value_view(l) > value_view(r); } +#else +bool operator==(const value_view &, const value_view); +bool operator!=(const value_view &, const value_view); +bool operator<=(const value_view &, const value_view); +bool operator< (const value_view &, const value_view); +bool operator> (const value_view &, const value_view); +bool operator>=(const value_view &, const value_view); +#endif struct key_value_pair { @@ -1118,6 +1142,8 @@ struct key_value_pair string_type value_; }; +#if !defined(GENERATING_DOCUMENTATION) + template typename std::enable_if< ((std::is_same::value || std::is_same::value) && @@ -1178,6 +1204,17 @@ typename std::enable_if< bool>::type operator >(const T &l, const U & r) { return key_value_pair_view(l) > key_value_pair_view(r); } +#else + +bool operator==(const key_value_pair_view &, const key_value_pair_view); +bool operator!=(const key_value_pair_view &, const key_value_pair_view); +bool operator<=(const key_value_pair_view &, const key_value_pair_view); +bool operator< (const key_value_pair_view &, const key_value_pair_view); +bool operator> (const key_value_pair_view &, const key_value_pair_view); +bool operator>=(const key_value_pair_view &, const key_value_pair_view); + +#endif + template<> inline key_view key_value_pair::get<0u>() const @@ -1254,7 +1291,7 @@ namespace environment * void dump_my_env(current_view env = current()) * { * for (auto & [k, v] : env) -* std::cout << k.string() << " = " << v.string() << std::endl; + * std::cout << k.string() << " = " << v.string() << std::endl; * } * * @endcode @@ -1319,6 +1356,55 @@ struct current_view /// Obtain a handle to the current environment inline current_view current() {return current_view();} +namespace detail +{ + +template +auto find_key(Environment & env, key_view ky) + -> typename std::enable_if::value, value_view>::type +{ + const auto itr = std::find_if(std::begin(env), std::end(env), + [&](key_value_pair_view vp) + { + auto tmp = std::get<0>(vp) == ky; + if (tmp) + return true; + else + return false; + }); + + if (itr != std::end(env)) + return key_value_pair_view(*itr).value(); + else + return {}; +} + +template +auto find_key(Environment & env, key_view ky) + -> typename std::enable_if< + !std::is_convertible::value && + std::is_convertible::value, + value>::type +{ + const auto itr = std::find_if(std::begin(env), std::end(env), + [&](key_value_pair vp) + { + auto tmp = std::get<0>(vp) == ky; + if (tmp) + return true; + else + return false; + }); + if (itr != std::end(env)) + return key_value_pair(*itr).value(); + else + return {}; +} + + +} + + /// Find the home folder in an environment-like type. /** * @param env The environment to search. Defaults to the current environment of this process @@ -1336,26 +1422,10 @@ inline current_view current() {return current_view();} template inline filesystem::path home(Environment && env = current()) { - auto find_key = [&](key_view ky) -> filesystem::path - { - const auto itr = std::find_if(std::begin(env), std::end(env), - [&](decltype(*std::begin(env)) vp) - { - auto tmp = std::get<0>(vp) == ky; - if (tmp) - return true; - else - return false; - }); - if (itr != nullptr) - return std::get<1>(*itr); - else - return ""; - }; #if defined(ASIO_WINDOWS) - return find_key(L"HOMEDRIVE") + find_key(L"HOMEPATH"); + return detail::find_key(env, L"HOMEDRIVE") + detail::find_key(env, L"HOMEPATH").native_string(); #else - return find_key("HOME"); + return detail::find_key(env, "HOME").native_string(); #endif } @@ -1379,26 +1449,10 @@ inline BOOST_PROCESS_V2_NAMESPACE::filesystem::path find_executable( BOOST_PROCESS_V2_NAMESPACE::filesystem::path name, Environment && env = current()) { - auto find_key = [&](key_view ky) -> value_view - { - const auto itr = std::find_if(std::begin(env), std::end(env), - [&](decltype(*std::begin(env)) vp) - { - auto tmp = std::get<0>(vp) == ky; - if (tmp) - return true; - else - return false; - }); - if (itr != nullptr) - return std::get<1>(*itr); - else - return value_view(); - }; #if defined(BOOST_PROCESS_V2_WINDOWS) - auto path = find_key(L"PATH"); - auto pathext = find_key(L"PATHEXT"); + auto path = detail::find_key(env, L"PATH"); + auto pathext = detail::find_key(env, L"PATHEXT"); for (auto pp_view : path) { // first check if it has the extension already @@ -1423,7 +1477,7 @@ inline BOOST_PROCESS_V2_NAMESPACE::filesystem::path find_executable( } } #else - for (auto pp_view : find_key("PATH")) + for (auto pp_view : detail::find_key(env, "PATH")) { auto p = BOOST_PROCESS_V2_NAMESPACE::filesystem::path(pp_view.begin(), pp_view.end()) / name; error_code ec; diff --git a/include/boost/process/v2/execute.hpp b/include/boost/process/v2/execute.hpp index b028cffec..6d0b1d4f2 100644 --- a/include/boost/process/v2/execute.hpp +++ b/include/boost/process/v2/execute.hpp @@ -15,6 +15,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE + /** * @brief Run a process and wait for it to complete. * diff --git a/include/boost/process/v2/popen.hpp b/include/boost/process/v2/popen.hpp index bdc467315..290582a5b 100644 --- a/include/boost/process/v2/popen.hpp +++ b/include/boost/process/v2/popen.hpp @@ -282,7 +282,7 @@ struct basic_popen : basic_process BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, std::size_t)) WriteToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(WriteToken, + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WriteToken, void (boost::system::error_code, std::size_t)) async_write_some(const ConstBufferSequence& buffers, BOOST_ASIO_MOVE_ARG(WriteToken) token @@ -397,7 +397,7 @@ struct basic_popen : basic_process BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, std::size_t)) ReadToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)> - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ReadToken, + BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(ReadToken, void (boost::system::error_code, std::size_t)) async_read_some(const MutableBufferSequence& buffers, BOOST_ASIO_MOVE_ARG(ReadToken) token diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index 0b56982a8..5b1f85e0e 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -81,20 +81,38 @@ BOOST_AUTO_TEST_CASE(environment) {"HOMEDRIVE", "X:"}, {"HOMEPATH", "\\users\\theodora"} }; + std::vector custom_env2 = + { + "HOME=/home/byzantium", + "HOMEDRIVE=X:", + "HOMEPATH=\\users\\theodora" + }; BOOST_CHECK_EQUAL(bpe::home(custom_env), "/home/byzantium"); + BOOST_CHECK_EQUAL(bpe::home(custom_env2), "/home/byzantium"); #else std::unordered_map custom_env = { - {"HOME", L"/home/byzantium"}, - {"HOMEDRIVE", L"X:"}, - {"HOMEPATH", L"\\users\\theodora"} + L"HOME", L"/home/byzantium", + L"HOMEDRIVE", L"X:", + L"HOMEPATH", L"\\users\\theodora" + }; + + std::vector custom_env2 = + { + {L"HOME=/home/byzantium"}, + {L"HOMEDRIVE=X:"}, + {L"HOMEPATH=\\users\\theodora"} }; BOOST_CHECK_EQUAL(bpe::home(custom_env), L"X:\\Users\\theodora"); + BOOST_CHECK_EQUAL(bpe::home(custom_env2), L"X:\\Users\\theodora"); #endif bp2::process_environment env{custom_env }; boost::ignore_unused(env); + + bp2::process_environment env2{custom_env2}; + boost::ignore_unused(env2); } From f2a036760596958a3287aa73970de7bcc187a171 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 4 Jun 2022 22:50:49 +0800 Subject: [PATCH 076/397] Minor buf fixes --- include/boost/process/v2/cstring_ref.hpp | 4 ++-- include/boost/process/v2/detail/config.hpp | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/boost/process/v2/cstring_ref.hpp b/include/boost/process/v2/cstring_ref.hpp index a3e146304..1fa55bb14 100644 --- a/include/boost/process/v2/cstring_ref.hpp +++ b/include/boost/process/v2/cstring_ref.hpp @@ -54,7 +54,7 @@ struct basic_cstring_ref using value_type = CharT; using traits_type = Traits; - BOOST_CONSTEXPR basic_cstring_ref() : view_(detail::null_char_(value_type{})) {} + BOOST_CONSTEXPR basic_cstring_ref() noexcept : view_(detail::null_char_(value_type{})) {} BOOST_CONSTEXPR basic_cstring_ref(std::nullptr_t) = delete; BOOST_CONSTEXPR basic_cstring_ref( const value_type* s ) : view_(s) {} @@ -157,7 +157,7 @@ struct basic_cstring_ref return traits_type::eq(view_[0], x); } - BOOST_CONSTEXPR size_type find( CharT ch, size_type pos = 0 ) const BOOST_NOEXCEPT + BOOST_CXX14_CONSTEXPR size_type find( CharT ch, size_type pos = 0 ) const BOOST_NOEXCEPT { for (auto p = view_ + pos; *p != *null_char_(); p++) if (traits_type::eq(*p, ch)) diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp index dabbca99d..7e4fe58f4 100644 --- a/include/boost/process/v2/detail/config.hpp +++ b/include/boost/process/v2/detail/config.hpp @@ -133,9 +133,14 @@ BOOST_PROCESS_V2_END_NAMESPACE #if defined(BOOST_PROCESS_V2_POSIX) #if defined(__linux__) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN) + +#include + +#if defined(SYS_pidfd_open) #define BOOST_PROCESS_V2_PIDFD_OPEN 1 #define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1 #endif +#endif #if defined(__FreeBSD__) && !defined(BOOST_PROCESS_V2_DISABLE_PDFORK) #define BOOST_PROCESS_V2_PDFORK 1 From fb48747fc8634420f8db5ef279140558849eb688 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 4 Jun 2022 23:26:14 +0800 Subject: [PATCH 077/397] Another cstring_ref --- include/boost/process/v2/cstring_ref.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/v2/cstring_ref.hpp b/include/boost/process/v2/cstring_ref.hpp index 1fa55bb14..609b951f3 100644 --- a/include/boost/process/v2/cstring_ref.hpp +++ b/include/boost/process/v2/cstring_ref.hpp @@ -140,7 +140,7 @@ struct basic_cstring_ref traits_type::to_int_type(x[idx]); // will compare to null char of either. } - BOOST_CONSTEXPR bool starts_with(string_view_type x) const BOOST_NOEXCEPT + BOOST_CXX14_CONSTEXPR bool starts_with(string_view_type x) const BOOST_NOEXCEPT { if (x.empty()) return true; From 062ac9beb2ef04f15f735d18be774a345a0bf814 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 01:55:28 +0800 Subject: [PATCH 078/397] Added limit_fd functionality --- doc/v2/introduction.qbk | 14 +- doc/v2/launcher.qbk | 2 + .../process/v2/impl/default_launcher.ipp | 2 + include/boost/process/v2/posix/bind_fd.hpp | 5 +- .../process/v2/posix/default_launcher.hpp | 17 +- .../process/v2/posix/detail/close_handles.hpp | 29 +++ .../process/v2/posix/detail/close_handles.ipp | 197 ++++++++++++++++++ .../v2/posix/fork_and_forget_launcher.hpp | 2 + .../process/v2/posix/pdfork_launcher.hpp | 5 + .../boost/process/v2/posix/vfork_launcher.hpp | 2 + include/boost/process/v2/stdio.hpp | 5 + 11 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 include/boost/process/v2/posix/detail/close_handles.hpp create mode 100644 include/boost/process/v2/posix/detail/close_handles.ipp diff --git a/doc/v2/introduction.qbk b/doc/v2/introduction.qbk index 05ffca0d1..2554d3f82 100644 --- a/doc/v2/introduction.qbk +++ b/doc/v2/introduction.qbk @@ -11,6 +11,7 @@ The major changes are * Removed unreliable functionality * UTF8 Support * separate compilation +* fd safe by default [section:simplified Simplified Interface] @@ -56,8 +57,7 @@ file-handles for the subprocess. Certain parts of boost.process were not as reliable as they should've been. -This is true of `limit_handles` which cannot be done safely on posix (it only finds a subset of FDs), -and especially for the `wait_for`` and `wait_until` functions on the process. +This concerns especially the `wait_for`` and `wait_until` functions on the process. The latter are easy to do on windows, but posix does not provide an API for this. Thus the wait_for used signals or fork, which was all but safe. Since process v2 is based on asio and thus supports cancellation, @@ -81,4 +81,14 @@ It can be achieved by defining `BOOST_PROCESS_V2_SEPARATE_COMPILATION` and inclu [endsect] +[section:limit_fd Fd safe by default] + +While not a problem on windows (since HANDLEs get manually enabled for inheritance), +posix systems create a problem with inheriting file handles by default. + +Process V2 will automatically close all non-whitelisted descriptors, +without needing any option to enable it. + +[endsect] + [endsect] diff --git a/doc/v2/launcher.qbk b/doc/v2/launcher.qbk index a47734b57..4e25728e8 100644 --- a/doc/v2/launcher.qbk +++ b/doc/v2/launcher.qbk @@ -88,6 +88,8 @@ The call sequence when exec fails: ''' +The launcher will close all non-whitelisted file descriptors after `on_exec_setup`. + [endsect] [section:windows Windows Launchers] diff --git a/include/boost/process/v2/impl/default_launcher.ipp b/include/boost/process/v2/impl/default_launcher.ipp index 1282ad523..26a897940 100644 --- a/include/boost/process/v2/impl/default_launcher.ipp +++ b/include/boost/process/v2/impl/default_launcher.ipp @@ -15,6 +15,8 @@ #if defined(BOOST_PROCESS_V2_WINDOWS) #include +#else +#include #endif diff --git a/include/boost/process/v2/posix/bind_fd.hpp b/include/boost/process/v2/posix/bind_fd.hpp index 1530210eb..bb0ab08b9 100644 --- a/include/boost/process/v2/posix/bind_fd.hpp +++ b/include/boost/process/v2/posix/bind_fd.hpp @@ -96,8 +96,9 @@ struct bind_fd { if (::dup2(fd, target) == -1) return error_code(errno, system_category()); - else - return error_code (); + + launcher.fd_whitelist.push_back(target); + return error_code (); } }; diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index 4af301987..6ae474bd4 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #if defined(BOOST_PROCESS_V2_STANDALONE) @@ -294,6 +295,9 @@ struct default_launcher /// The pid of the subprocess - will be assigned after fork. int pid = -1; + /// The whitelist for file descriptors. + std::vector fd_whitelist; + default_launcher() = default; template @@ -390,8 +394,12 @@ struct default_launcher { ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); ::close(pg.p[0]); - ec = detail::on_exec_setup(*this, executable, argv, inits...); + if (!ec) + { + fd_whitelist.push_back(pg.p[1]); + close_all_fds(ec); + } if (!ec) ::execve(executable.c_str(), const_cast(argv), const_cast(env)); @@ -431,6 +439,13 @@ struct default_launcher } protected: + void close_all_fds(error_code & ec) + { + std::sort(fd_whitelist.begin(), fd_whitelist.end()); + detail::close_all(fd_whitelist, ec); + fd_whitelist.clear(); + } + struct pipe_guard { int p[2]; diff --git a/include/boost/process/v2/posix/detail/close_handles.hpp b/include/boost/process/v2/posix/detail/close_handles.hpp new file mode 100644 index 000000000..86c1e9d0f --- /dev/null +++ b/include/boost/process/v2/posix/detail/close_handles.hpp @@ -0,0 +1,29 @@ +// 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_DETAIL_CLOSE_HANDLES_HPP +#define BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_HPP + +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace posix +{ + +namespace detail +{ + +// whitelist must be ordered +BOOST_PROCESS_V2_DECL void close_all(const std::vector & whitelist, + error_code & ec); + +} + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_HPP diff --git a/include/boost/process/v2/posix/detail/close_handles.ipp b/include/boost/process/v2/posix/detail/close_handles.ipp new file mode 100644 index 000000000..246f129c5 --- /dev/null +++ b/include/boost/process/v2/posix/detail/close_handles.ipp @@ -0,0 +1,197 @@ +// +// boost/process/v2/poxix/detail/close_handles.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net) +// +// 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_DETAIL_CLOSE_HANDLES_IPP +#define BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP + +#include +#include +#include +// linux has close_range since 5.19 + + +#if defined(__NetBSD__) + || defined(__FreeBSD__) + || defined(__APPLE__) + || defined(__MACH__) + +// https://www.freebsd.org/cgi/man.cgi?query=close_range&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html +// https://man.netbsd.org/closefrom.3 +// __FreeBSD__ +// +// gives us +// +// int closefrom(int fd); +// int close_range(u_int lowfd, u_int highfd, int flags); + +#include +#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM 1 + +#elif defined(__sun) + +/*https://docs.oracle.com/cd/E36784_01/html/E36874/closefrom-3c.html + +int fdwalk(int (*func)(void *, int), void *cd); +*/ + +#include +#define BOOST_PROCESS_V2_HAS_PDFORK 1 + +#elif defined(__linux__) + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0) + +// https://man7.org/linux/man-pages/man2/close_range.2.html +#include +#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1 +#else + +#include + +#endif + +#else + +#include + +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +namespace posix +{ + +namespace detail +{ + +#if defined(BOOST_PROCESS_V2_HAS_PDFORK) + +void close_all(const std::vector & whitelist, error_code & ec) +{ + fdwalk(+[](void * p, int fd) + { + const auto & wl = *static_cast*>(p); + if (std::find(wl.begin(), wl.end(), fd) == wl.end()) + return ::close(fd); + else + return 0; + }, const_cast(static_cast(&whitelist)) ); + ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); +} + +#elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM) + +// freeBSD impl - whitelist must be ordered +void close_all(const std::vector & whitelist, error_code & ec) +{ + //the most common scenario is whitelist = {0,1,2} + if (!whitelist.empty()) + { + if (whitelist.front() != 0) + ::close_range(0, whitelist.front() - 1, CLOSE_RANGE_UNSHARE); + + for (std::size_t idx = 0u; + idx < (whitelist.size() - 1u); + idx++) + { + const auto mine = whitelist[idx]; + const auto next = whitelist[idx]; + if ((mine + 1) != next && (mine != next)) + { + ::close_range(mine + 1, next - 1, CLOSE_RANGE_UNSHARE); + } + } + + ::closefrom(whitelist.back() + 1); + } + else + ::closefrom(0); +} + +#elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE) + + +// linux impl - whitelist must be ordered +void close_all(const std::vector & whitelist, error_code & ec) +{ +// https://patchwork.kernel.org/project/linux-fsdevel/cover/20200602204219.186620-1-christian.brauner@ubuntu.com/ + //the most common scenario is whitelist = {0,1,2} + if (!whitelist.empty()) + { + if (whitelist.front() != 0) + ::close_range(0, whitelist.front() - 1, CLOSE_RANGE_UNSHARE); + + for (std::size_t idx = 0u; + idx < (whitelist.size() - 1u); + idx++) + { + const auto mine = whitelist[idx]; + const auto next = whitelist[idx]; + if ((mine + 1) != next && (mine != next)) + { + ::close_range(mine + 1, next - 1, CLOSE_RANGE_UNSHARE); + } + } + + ::close_range(whitelist.back() + 1, std::numeric_limits::max(), CLOSE_RANGE_UNSHARE); + } + else + ::close_range(0, std::numeric_limits::max(), CLOSE_RANGE_UNSHARE); +} + +#else + +// default one +void close_all(const std::vector & whitelist, error_code & ec) +{ + std::unique_ptr dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}}; + if (dir.get() == nullptr) + { + ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); + return ; + } + + auto dir_fd = ::dirfd(dir.get()); + if (dir_fd == -1) + { + ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); + return ; + } + + 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 == dir_fd + || (std::find(whitelist.begin(), whitelist.end(), conv) != whitelist.end())) + continue; + + ::close(conv); + } +} + +#endif + +} + +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP \ No newline at end of file diff --git a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp index 383e33af0..e0fb63c43 100644 --- a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp +++ b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp @@ -101,6 +101,8 @@ struct fork_and_forget_launcher : default_launcher ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); 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)); diff --git a/include/boost/process/v2/posix/pdfork_launcher.hpp b/include/boost/process/v2/posix/pdfork_launcher.hpp index fad30ece9..cafddfdeb 100644 --- a/include/boost/process/v2/posix/pdfork_launcher.hpp +++ b/include/boost/process/v2/posix/pdfork_launcher.hpp @@ -118,6 +118,11 @@ struct pdfork_launcher : default_launcher ::close(pg.p[0]); ec = detail::on_exec_setup(*this, executable, argv, inits...); + if (!ec) + { + fd_whitelist.push_back(pg.p[1]); + close_all_fds(ec); + } if (!ec) ::execve(executable.c_str(), const_cast(argv), const_cast(env)); diff --git a/include/boost/process/v2/posix/vfork_launcher.hpp b/include/boost/process/v2/posix/vfork_launcher.hpp index 48fe78697..341dbe620 100644 --- a/include/boost/process/v2/posix/vfork_launcher.hpp +++ b/include/boost/process/v2/posix/vfork_launcher.hpp @@ -102,6 +102,8 @@ struct vfork_launcher : default_launcher { ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); 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)); diff --git a/include/boost/process/v2/stdio.hpp b/include/boost/process/v2/stdio.hpp index fd43eacfd..5e0320eef 100644 --- a/include/boost/process/v2/stdio.hpp +++ b/include/boost/process/v2/stdio.hpp @@ -289,6 +289,11 @@ struct process_stdio if (::dup2(err.fd, err.target) == -1) return error_code(errno, system_category()); + + launcher.fd_whitelist.push_back(STDIN_FILENO); + launcher.fd_whitelist.push_back(STDOUT_FILENO); + launcher.fd_whitelist.push_back(STDERR_FILENO); + return error_code {}; }; #endif From f0c98aa97f878110479af2ab84f08ff1d22c5431 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 02:08:04 +0800 Subject: [PATCH 079/397] Multiple fixes --- include/boost/process/v2/cstring_ref.hpp | 2 +- .../v2/detail/process_handle_signal.hpp | 6 ++--- include/boost/process/v2/environment.hpp | 22 ++++++++++++++----- include/boost/process/v2/impl/error.ipp | 3 --- .../process/v2/posix/default_launcher.hpp | 2 +- .../v2/posix/fork_and_forget_launcher.hpp | 2 +- .../process/v2/posix/pdfork_launcher.hpp | 2 +- .../boost/process/v2/posix/vfork_launcher.hpp | 2 +- include/boost/process/v2/process.hpp | 5 +++-- 9 files changed, 27 insertions(+), 19 deletions(-) diff --git a/include/boost/process/v2/cstring_ref.hpp b/include/boost/process/v2/cstring_ref.hpp index 609b951f3..72c544487 100644 --- a/include/boost/process/v2/cstring_ref.hpp +++ b/include/boost/process/v2/cstring_ref.hpp @@ -103,7 +103,7 @@ struct basic_cstring_ref BOOST_ATTRIBUTE_NODISCARD BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT {return *view_ == *detail::null_char_(CharT{}); } BOOST_CONSTEXPR const_reference operator[](size_type pos) const {return view_[pos] ;} - BOOST_CONSTEXPR const_reference at(size_type pos) const + BOOST_CXX14_CONSTEXPR const_reference at(size_type pos) const { if (pos >= size()) throw std::out_of_range("cstring-view out of range"); diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index b2928c09e..f27faa6d1 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #else #include #include @@ -81,11 +81,11 @@ struct basic_process_handle_signal handle.pid_ = -1; } - basic_process_handle_win(basic_process_handle_win && handle) + basic_process_handle_signal(basic_process_handle_signal && handle) { pid_ = handle.id(); signal_set_ = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set(handle.get_executor(), SIGCHLD); - handle.pid_ = static_cast(-1); + handle.pid_ = -1; return *this; } diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index 4252fd79a..2d35ca5af 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -1235,13 +1235,21 @@ namespace std { template<> -struct tuple_size : integral_constant {}; +class tuple_size : integral_constant {}; template<> -struct tuple_element<0u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair> {using type = BOOST_PROCESS_V2_NAMESPACE::environment::key_view;}; +class tuple_element<0u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair> +{ + public: + using type = BOOST_PROCESS_V2_NAMESPACE::environment::key_view; +}; template<> -struct tuple_element<1u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair> {using type = BOOST_PROCESS_V2_NAMESPACE::environment::value_view;}; +class tuple_element<1u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair> +{ + public: + using type = BOOST_PROCESS_V2_NAMESPACE::environment::value_view; +}; template inline auto get(const BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair & kvp) @@ -1251,17 +1259,19 @@ inline auto get(const BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair & } template<> -struct tuple_size : integral_constant {}; +class tuple_size : integral_constant {}; template<> -struct tuple_element<0u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view> +class tuple_element<0u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view> { + public: using type = BOOST_PROCESS_V2_NAMESPACE::environment::key_view; }; template<> -struct tuple_element<1u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view> +class tuple_element<1u, BOOST_PROCESS_V2_NAMESPACE::environment::key_value_pair_view> { + public: using type = BOOST_PROCESS_V2_NAMESPACE::environment::value_view; }; diff --git a/include/boost/process/v2/impl/error.ipp b/include/boost/process/v2/impl/error.ipp index a8fa068d2..a5924df78 100644 --- a/include/boost/process/v2/impl/error.ipp +++ b/include/boost/process/v2/impl/error.ipp @@ -153,9 +153,6 @@ struct exit_code_category final : public error_category # if defined(SIGTTOU) case SIGTTOU: return "SIGTTOU: Terminal output for background process"; # endif -# if defined(SIGUNUSED) - case SIGUNUSED: return "SIGUNUSED: Synonymous with SIGSYS"; -# endif # if defined(SIGURG) case SIGURG: return "SIGURG: Urgent condition on socket (4.2BSD)"; # endif diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index 6ae474bd4..e683f4b56 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -432,7 +432,7 @@ struct default_launcher return basic_process{exec}; } } - basic_process proc{exec, pid}; + basic_process proc(exec, pid); detail::on_success(*this, executable, argv, ec, inits...); return proc; diff --git a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp index e0fb63c43..301746d96 100644 --- a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp +++ b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp @@ -118,7 +118,7 @@ struct fork_and_forget_launcher : default_launcher return basic_process{exec}; } } - basic_process proc{exec, pid}; + basic_process proc(exec, pid); detail::on_success(*this, executable, argv, ec, inits...); return proc; diff --git a/include/boost/process/v2/posix/pdfork_launcher.hpp b/include/boost/process/v2/posix/pdfork_launcher.hpp index cafddfdeb..329ecc10a 100644 --- a/include/boost/process/v2/posix/pdfork_launcher.hpp +++ b/include/boost/process/v2/posix/pdfork_launcher.hpp @@ -155,7 +155,7 @@ struct pdfork_launcher : default_launcher return basic_process{exec}; } } - basic_process proc{exec, pid, fd}; + basic_process proc(exec, pid, fd); detail::on_success(*this, executable, argv, ec, inits...); return proc; } diff --git a/include/boost/process/v2/posix/vfork_launcher.hpp b/include/boost/process/v2/posix/vfork_launcher.hpp index 341dbe620..06892be59 100644 --- a/include/boost/process/v2/posix/vfork_launcher.hpp +++ b/include/boost/process/v2/posix/vfork_launcher.hpp @@ -120,7 +120,7 @@ struct vfork_launcher : default_launcher return basic_process{exec}; } - basic_process proc{exec, pid}; + basic_process proc(exec, pid); detail::on_success(*this, executable, argv, ec, inits...); return proc; diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index 4f79a8bb7..fa20e0a11 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -283,10 +283,11 @@ struct basic_process if (!ec && !r) exit_status_ = exit_code; else - throw system_error(ec, "running failed"); + detail::throw_error(ec, "running failed"); return r; } + /// Throwing @overload bool running(error_code & ec) bool running(error_code & ec) noexcept { @@ -296,9 +297,9 @@ struct basic_process exit_status_ = exit_code; return r; } + /// Check if the process is referring to an existing process. /** Note that this might be a process that already exited.*/ - bool is_open() const { return process_handle_.is_open(); } /// Asynchronously wait for the process to exit and deliver the portable exit-code in the completion handler. From a911da2c1faa5b01dfb9fefec6243e06f1ebe641 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 02:35:49 +0800 Subject: [PATCH 080/397] More CI-driven test fixes --- include/boost/process/v2/detail/process_handle_fd.hpp | 4 ++-- .../process/v2/detail/process_handle_fd_or_signal.hpp | 4 ++-- .../boost/process/v2/detail/process_handle_signal.hpp | 4 ++-- .../boost/process/v2/detail/process_handle_windows.hpp | 2 +- include/boost/process/v2/process.hpp | 6 ++---- test/v2/environment.cpp | 9 ++++++--- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index d7bd3def2..b9f02bcfc 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -259,7 +259,7 @@ struct basic_process_handle_fd void operator()(Self &&self) { error_code ec; - native_exit_code_type exit_code; + native_exit_code_type exit_code{}; if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; else if (::waitpid(pid_, &exit_code, 0) == -1) @@ -291,7 +291,7 @@ struct basic_process_handle_fd template void operator()(Self &&self, error_code ec, int = 0) { - native_exit_code_type exit_code; + native_exit_code_type exit_code{}; if (!ec) if (::waitpid(pid_, &exit_code, 0) == -1) ec = get_last_error(); 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 1dec8f214..b8e55b603 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 @@ -263,7 +263,7 @@ struct basic_process_handle_fd_or_signal void operator()(Self &&self) { error_code ec; - native_exit_code_type exit_code; + native_exit_code_type exit_code{}; if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; else if (::waitpid(pid_, &exit_code, 0) == -1) @@ -298,7 +298,7 @@ struct basic_process_handle_fd_or_signal template void operator()(Self &&self, error_code ec, int = 0) { - native_exit_code_type exit_code; + native_exit_code_type exit_code{}; if (!ec) if (::waitpid(pid_, &exit_code, 0) == -1) ec = get_last_error(); diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index f27faa6d1..5f116ddf2 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -254,7 +254,7 @@ struct basic_process_handle_signal void operator()(Self &&self) { error_code ec; - native_exit_code_type exit_code; + native_exit_code_type exit_code{}; if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; else if (::waitpid(pid_, &exit_code, 0) == -1) @@ -284,7 +284,7 @@ struct basic_process_handle_signal template void operator()(Self &&self, error_code ec, int ) { - native_exit_code_type exit_code; + native_exit_code_type exit_code{}; if (!ec) if (::waitpid(pid_, &exit_code, 0) == -1) ec = get_last_error(); diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp index 03df1f4da..bd8441ab7 100644 --- a/include/boost/process/v2/detail/process_handle_windows.hpp +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -259,7 +259,7 @@ struct basic_process_handle_win template void operator()(Self &&self, error_code ec) { - native_exit_code_type exit_code; + native_exit_code_type exit_code{}; if (!ec) detail::get_exit_code_(handle.native_handle(), exit_code, ec); std::move(self).complete(ec, exit_code); diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index fa20e0a11..f1f03e53b 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -77,8 +77,6 @@ struct basic_process basic_process(basic_process&& lhs) : process_handle_(std::move(lhs.process_handle_)), exit_status_{lhs.exit_status_} - - { } @@ -278,7 +276,7 @@ struct basic_process bool running() { error_code ec; - native_exit_code_type exit_code; + native_exit_code_type exit_code{}; auto r = process_handle_.running(exit_code, ec); if (!ec && !r) exit_status_ = exit_code; @@ -291,7 +289,7 @@ struct basic_process /// Throwing @overload bool running(error_code & ec) bool running(error_code & ec) noexcept { - native_exit_code_type exit_code ; + native_exit_code_type exit_code{}; auto r = process_handle_.running(exit_code, ec); if (!ec && !r) exit_status_ = exit_code; diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index 5b1f85e0e..610c6db39 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -32,11 +32,14 @@ BOOST_AUTO_TEST_CASE(environment) #if defined(BOOST_PROCESS_V2_WINDOWS) const auto key2 = "BP2_TeSt_NamE_\321\200\320\270\320\261\320\260"; // риба #else - const auto key2 = key1; + const auto key2 = key1; #endif - - BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); bp2::error_code ec; + bpe::unset(key1, ec); + bpe::unset(key2, ec); + ec.clear(); + + BOOST_CHECK_THROW(bpe::get(key1) , bp2::system_error); bpe::get(key2, ec); BOOST_CHECK(ec); ec.clear(); From d60ea9c4d3f4f797bd11a5a8e161d7ab035ea630 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 02:39:41 +0800 Subject: [PATCH 081/397] environ fix for apple --- include/boost/process/v2/detail/environment_posix.hpp | 5 ++++- include/boost/process/v2/posix/default_launcher.hpp | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/boost/process/v2/detail/environment_posix.hpp b/include/boost/process/v2/detail/environment_posix.hpp index bcfc47607..4154e3406 100644 --- a/include/boost/process/v2/detail/environment_posix.hpp +++ b/include/boost/process/v2/detail/environment_posix.hpp @@ -14,7 +14,10 @@ #include #include -#if defined(__FreeBSD__) +#if defined(__NetBSD__) + || defined(__FreeBSD__) + || defined(__APPLE__) + || defined(__MACH__) extern "C" { extern char **environ; } #endif diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index e683f4b56..f45e8f5d4 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -27,7 +27,11 @@ #include #include -#if defined(__FreeBSD__) + +#if defined(__NetBSD__) + || defined(__FreeBSD__) + || defined(__APPLE__) + || defined(__MACH__) extern "C" { extern char **environ; } #endif From 4e2e580b4c6943ebb24a2d426330395adbd587e2 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 14:58:00 +0800 Subject: [PATCH 082/397] Multiple non-linux fixes --- .../process/v2/detail/environment_posix.hpp | 5 +--- .../v2/detail/impl/environment_posix.ipp | 2 +- include/boost/process/v2/detail/impl/utf8.ipp | 0 .../v2/detail/process_handle_fd_or_signal.hpp | 28 +++++++++++++++++-- .../v2/detail/process_handle_signal.hpp | 4 +-- .../process/v2/posix/default_launcher.hpp | 5 +--- .../process/v2/posix/detail/close_handles.ipp | 9 ++---- 7 files changed, 33 insertions(+), 20 deletions(-) mode change 100755 => 100644 include/boost/process/v2/detail/impl/utf8.ipp diff --git a/include/boost/process/v2/detail/environment_posix.hpp b/include/boost/process/v2/detail/environment_posix.hpp index 4154e3406..237a87395 100644 --- a/include/boost/process/v2/detail/environment_posix.hpp +++ b/include/boost/process/v2/detail/environment_posix.hpp @@ -14,10 +14,7 @@ #include #include -#if defined(__NetBSD__) - || defined(__FreeBSD__) - || defined(__APPLE__) - || defined(__MACH__) +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) extern "C" { extern char **environ; } #endif diff --git a/include/boost/process/v2/detail/impl/environment_posix.ipp b/include/boost/process/v2/detail/impl/environment_posix.ipp index 061ead334..7d9ad564a 100644 --- a/include/boost/process/v2/detail/impl/environment_posix.ipp +++ b/include/boost/process/v2/detail/impl/environment_posix.ipp @@ -34,7 +34,7 @@ basic_cstring_ref> get( auto res = ::getenv(key.c_str()); if (res == nullptr) { - ec = ::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(); + ec.assign(ENOENT, system_category()); return {}; } return res; diff --git a/include/boost/process/v2/detail/impl/utf8.ipp b/include/boost/process/v2/detail/impl/utf8.ipp old mode 100755 new mode 100644 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 b8e55b603..328b9af8b 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 @@ -58,11 +58,34 @@ struct basic_process_handle_fd_or_signal typename std::enable_if< std::is_convertible::value - >::type = 0) + >::type * = nullptr) : pid_(-1), descriptor_(context) { } + template + basic_process_handle_fd_or_signal(ExecutionContext &context, + pid_type pid, + typename std::enable_if< + std::is_convertible::value + >::type * = nullptr) + : pid_(pid), descriptor_(context) + { + } + + template + basic_process_handle_fd_or_signal(ExecutionContext &context, + pid_type pid, native_handle_type process_handle, + typename std::enable_if< + std::is_convertible::value + >::type * = nullptr) + : pid_(pid), descriptor_(context, process_handle) + { + } + + basic_process_handle_fd_or_signal(Executor executor) : pid_(-1), descriptor_(executor) { @@ -84,12 +107,11 @@ struct basic_process_handle_fd_or_signal { handle.pid_ = -1; } - + // Warn: does not change the executor of the signal-set. basic_process_handle_fd_or_signal& operator=(basic_process_handle_fd_or_signal &&handle) { pid_ = handle.pid_; descriptor_ = std::move(handle.descriptor_); - signal_set_ = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set(handle.get_executor(), SIGCHLD); handle.pid_ = -1; return *this; } diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index 5f116ddf2..c6a01ac13 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -60,7 +60,7 @@ struct basic_process_handle_signal typename std::enable_if< std::is_convertible::value - >::type = 0) + >::type * = nullptr) : pid_(-1), signal_set_(context, SIGCHLD) { } @@ -84,7 +84,7 @@ struct basic_process_handle_signal basic_process_handle_signal(basic_process_handle_signal && handle) { pid_ = handle.id(); - signal_set_ = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set(handle.get_executor(), SIGCHLD); + //signal_set_ = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set(handle.get_executor(), SIGCHLD); handle.pid_ = -1; return *this; } diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index f45e8f5d4..3dca8f6e9 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -28,10 +28,7 @@ #include -#if defined(__NetBSD__) - || defined(__FreeBSD__) - || defined(__APPLE__) - || defined(__MACH__) +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) extern "C" { extern char **environ; } #endif diff --git a/include/boost/process/v2/posix/detail/close_handles.ipp b/include/boost/process/v2/posix/detail/close_handles.ipp index 246f129c5..a491ae6bb 100644 --- a/include/boost/process/v2/posix/detail/close_handles.ipp +++ b/include/boost/process/v2/posix/detail/close_handles.ipp @@ -17,10 +17,7 @@ // linux has close_range since 5.19 -#if defined(__NetBSD__) - || defined(__FreeBSD__) - || defined(__APPLE__) - || defined(__MACH__) +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) // https://www.freebsd.org/cgi/man.cgi?query=close_range&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html // https://man.netbsd.org/closefrom.3 @@ -97,7 +94,7 @@ void close_all(const std::vector & whitelist, error_code & ec) if (!whitelist.empty()) { if (whitelist.front() != 0) - ::close_range(0, whitelist.front() - 1, CLOSE_RANGE_UNSHARE); + ::close_range(0, whitelist.front() - 1, 0); for (std::size_t idx = 0u; idx < (whitelist.size() - 1u); @@ -107,7 +104,7 @@ void close_all(const std::vector & whitelist, error_code & ec) const auto next = whitelist[idx]; if ((mine + 1) != next && (mine != next)) { - ::close_range(mine + 1, next - 1, CLOSE_RANGE_UNSHARE); + ::close_range(mine + 1, next - 1, 0); } } From faad3fa4df4b85aaf9fa6ab26febe3b1dfe4596b Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 15:36:04 +0800 Subject: [PATCH 083/397] More fixes --- include/boost/process/v2/detail/process_handle_signal.hpp | 2 +- include/boost/process/v2/posix/detail/close_handles.ipp | 2 +- test/v2/environment.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index c6a01ac13..14dc6a578 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -81,7 +81,7 @@ struct basic_process_handle_signal handle.pid_ = -1; } - basic_process_handle_signal(basic_process_handle_signal && handle) + basic_process_handle_signal& operator=(basic_process_handle_signal && handle) { pid_ = handle.id(); //signal_set_ = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set(handle.get_executor(), SIGCHLD); diff --git a/include/boost/process/v2/posix/detail/close_handles.ipp b/include/boost/process/v2/posix/detail/close_handles.ipp index a491ae6bb..a74409e6d 100644 --- a/include/boost/process/v2/posix/detail/close_handles.ipp +++ b/include/boost/process/v2/posix/detail/close_handles.ipp @@ -17,7 +17,7 @@ // linux has close_range since 5.19 -#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) // https://www.freebsd.org/cgi/man.cgi?query=close_range&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html // https://man.netbsd.org/closefrom.3 diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index 610c6db39..168a0b97b 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -23,7 +23,7 @@ namespace bpe = boost::process::v2::environment; BOOST_AUTO_TEST_CASE(environment) { - for (const auto & elem : bpe::get("PATH")) + for (auto && elem : bpe::get("PATH")) BOOST_CHECK(std::find(elem.begin(), elem.end(), bpe::delimiter) == elem.end()); BOOST_CHECK(bpe::get("PATH").size() > 0); @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(environment) BOOST_AUTO_TEST_CASE(wenvironment) { - for (const auto & elem : bpe::get(L"PATH")) + for (auto && elem : bpe::get(L"PATH")) BOOST_CHECK(std::find(elem.begin(), elem.end(), bpe::delimiter) == elem.end()); BOOST_CHECK(bpe::get(L"PATH").size() > 0); From 693a33010da3f2ea3629d0048e5fda87fac08365 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 15:59:09 +0800 Subject: [PATCH 084/397] Added more logs to tests --- test/v2/process.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 61e1c7458..35d9ca866 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -107,13 +107,13 @@ BOOST_AUTO_TEST_CASE(exit_code_async) bpv::process proc5(ctx, pth, {"sleep", "100"});; - proc1.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); + proc1.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); bpv::async_execute( bpv::process(ctx, pth, {"exit-code", "1"}), - [&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 1);}); - proc3.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 2);}); - proc4.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 42);}); - proc5.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); + [&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 1);}); + proc3.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 2);}); + proc4.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 42);}); + proc5.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); bpv::async_execute( bpv::process(ctx, pth, {"sleep", "100"}), [&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); @@ -168,7 +168,7 @@ BOOST_AUTO_TEST_CASE(interrupt) void trim_end(std::string & str) { - auto itr = std::find_if(str.rbegin(), str.rend(), [](char c) {return !std::isspace(c);}); + auto itr = std::find_if(str.rbegin(), str.rend(), &std::char_traits::not_eof); str.erase(itr.base(), str.end()); } @@ -406,10 +406,11 @@ std::string read_env(const char * name, Inits && ... inits) std::string out; bpv::error_code ec; - asio::read(rp, asio::dynamic_buffer(out), ec); + const auto sz = asio::read(rp, asio::dynamic_buffer(out), ec); BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); - + out.resize(sz); trim_end(out); + printf("Read env %s: '%s'\n", name, out.c_str()); proc.wait(); BOOST_CHECK_EQUAL(proc.exit_code(), 0); From f453d93e83869cc0a5632c63ee73b6222d43ac3d Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 16:09:24 +0800 Subject: [PATCH 085/397] Set pth in env test to absolute --- test/v2/process.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 35d9ca866..0f53510a1 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -391,7 +391,8 @@ template std::string read_env(const char * name, Inits && ... inits) { using boost::unit_test::framework::master_test_suite; - const auto pth = master_test_suite().argv[1]; + const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]); + asio::io_context ctx; From 548ea7d9997917278c7b4d058135c57163833cbf Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 16:54:52 +0800 Subject: [PATCH 086/397] Process env test fixes --- include/boost/process/v2/environment.hpp | 2 +- test/v2/process.cpp | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index 2d35ca5af..2016a6d9a 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -55,7 +55,7 @@ using char_type = implementation_defined ; constexpr char_type equality_sign = implementation_defined; /// The delimiter in environemtn lists. Commonly used by the `PATH` variable. -constexpr char_type equality_sign = implementation_defined; +constexpr char_type delimiter = implementation_defined; /// The native handle of an environment. Note that this can be an owning pointer and is generally not thread safe. using native_handle = implementation_defined; diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 0f53510a1..074a2ed04 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -421,16 +421,22 @@ std::string read_env(const char * name, Inits && ... inits) BOOST_AUTO_TEST_CASE(environment) { + std::string path = ::getenv("PATH"); + using std::operator""s; BOOST_CHECK_EQUAL(read_env("PATH"), ::getenv("PATH")); - BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{"FOOBAR=FOO-BAR"})); - BOOST_CHECK_EQUAL("BAR-FOO", read_env("PATH", bpv::process_environment{"PATH=BAR-FOO", "XYZ=ZYX"})); - BOOST_CHECK_EQUAL("BAR-FOO", read_env("PATH", bpv::process_environment{"PATH=BAR-FOO", "XYZ=ZYX"})); + path = "PATH=" + path; + BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{"FOOBAR=FOO-BAR", path.c_str()})); + path += static_cast(bpv::environment::delimiter); + path += "/bar/foo"; + BOOST_CHECK_EQUAL(path.substr(5), read_env("PATH", bpv::process_environment{path.c_str(), "XYZ=ZYX"})); #if defined(BOOST_PROCESS_V2_WINDOWS) - BOOST_CHECK_EQUAL("BAR-FOO", read_env("PATH", bpv::process_environment{L"PATH=BAR-FOO", L"XYZ=ZYX"})); - BOOST_CHECK_EQUAL("BAR-FOO", read_env("PATH", bpv::process_environment{L"PATH=BAR-FOO", L"XYZ=ZYX"})); - BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{L"FOOBAR=FOO-BAR"})); + std::wstring wpath = L"PATh=" + std::wstring(_wgetenv(L"PatH")); + BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{L"FOOBAR=FOO-BAR", wpath.c_str()})); + wpath += bpv::environment::delimiter; + wpath += L"C:\\bar\\foo"; + BOOST_CHECK_EQUAL(wpath.substr(5), read_env("pATH", bpv::process_environment{wpath.c_str(), std::wstring(L"XYZ=ZYX")})); #endif BOOST_CHECK_EQUAL(read_env("PATH", bpv::process_environment(bpv::environment::current())), ::getenv("PATH")); From b9420be9815f39f4444d845cb000788c7d78da8f Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 17:47:37 +0800 Subject: [PATCH 087/397] removed usin ""s --- test/v2/process.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 074a2ed04..2249871d2 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -422,7 +422,6 @@ std::string read_env(const char * name, Inits && ... inits) BOOST_AUTO_TEST_CASE(environment) { std::string path = ::getenv("PATH"); - using std::operator""s; BOOST_CHECK_EQUAL(read_env("PATH"), ::getenv("PATH")); path = "PATH=" + path; From f56e42fd2ec73893c57a6f60d1baba7de0c1c6d5 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 18:24:01 +0800 Subject: [PATCH 088/397] Added diagnostic for CI --- test/v2/target.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/v2/target.cpp b/test/v2/target.cpp index 852f33f1d..deffe0322 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -3,6 +3,7 @@ #include #include +extern char **environ; #if defined(BOOST_PROCESS_V2_WINDOWS) #include @@ -54,7 +55,15 @@ int main(int argc, char * argv[]) else if (mode == "print-env") { auto p = ::getenv(argv[2]); - assert(printf("%s", p) > 0); + if (p) + assert(printf("%s", p) > 0); + else + { + printf("Can't find %s in environment\n", argv[2]); + for (auto e = environ; e != nullptr; e++) + printf(" %s\n", *e); + return 1; + } } #if defined(BOOST_PROCESS_V2_WINDOWS) else if (mode == "showwindow") From f90edf44e14b180951e9b0edcc5f50a43d685658 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 20:45:20 +0800 Subject: [PATCH 089/397] temporarily ignoring close_all_fds for diagnostics --- include/boost/process/v2/posix/default_launcher.hpp | 2 +- test/v2/target.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index 3dca8f6e9..fef016c6f 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -399,7 +399,7 @@ struct default_launcher if (!ec) { fd_whitelist.push_back(pg.p[1]); - close_all_fds(ec); + //close_all_fds(ec); } if (!ec) ::execve(executable.c_str(), const_cast(argv), const_cast(env)); diff --git a/test/v2/target.cpp b/test/v2/target.cpp index deffe0322..4449da007 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -55,7 +55,7 @@ int main(int argc, char * argv[]) else if (mode == "print-env") { auto p = ::getenv(argv[2]); - if (p) + if (p && *p) assert(printf("%s", p) > 0); else { From 4dfc1bd4fd38be6158ee3d0b14c3e93102a56aaf Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 5 Jun 2022 22:00:19 +0800 Subject: [PATCH 090/397] Fixed waitpid in the async_wait --- .../boost/process/v2/detail/process_handle_fd.hpp | 11 ++++++++--- .../v2/detail/process_handle_fd_or_signal.hpp | 11 ++++++++--- .../process/v2/detail/process_handle_signal.hpp | 14 +++++++++----- test/v2/Jamfile.jam | 4 ++-- test/v2/process.cpp | 2 -- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index b9f02bcfc..75f3eb965 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -260,12 +260,17 @@ struct basic_process_handle_fd { error_code ec; native_exit_code_type exit_code{}; + int wait_res = -1; if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; - else if (::waitpid(pid_, &exit_code, 0) == -1) - ec = get_last_error(); + else + { + wait_res = ::waitpid(pid_, &exit_code, WNOHANG); + if (wait_res == -1) + ec = get_last_error(); + } - if (!ec && process_is_running(exit_code)) + if (!ec && (wait_res == 0)) { descriptor.async_wait( BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::descriptor_base::wait_read, std::move(self)); 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 328b9af8b..e427f5066 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 @@ -286,12 +286,17 @@ struct basic_process_handle_fd_or_signal { error_code ec; native_exit_code_type exit_code{}; + int wait_res = -1; if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; - else if (::waitpid(pid_, &exit_code, 0) == -1) - ec = get_last_error(); + else + { + wait_res = ::waitpid(pid_, &exit_code, WNOHANG); + if (wait_res == -1) + ec = get_last_error(); + } - if (!ec && process_is_running(exit_code)) + if (!ec && (wait_res == 0)) { if (descriptor.is_open()) descriptor.async_wait( diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index 14dc6a578..f9785fc86 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -199,7 +199,7 @@ struct basic_process_handle_signal if (pid_ <= 0) return false; int code = 0; - int res = ::waitpid(pid_, &code, 0); + int res = ::waitpid(pid_, &code, WNOHANG); if (res == -1) ec = get_last_error(); else @@ -255,12 +255,16 @@ struct basic_process_handle_signal { error_code ec; native_exit_code_type exit_code{}; + int wait_res = -1; if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; - else if (::waitpid(pid_, &exit_code, 0) == -1) - ec = get_last_error(); - - if (!ec && process_is_running(exit_code)) + else + { + wait_res = ::waitpid(pid_, &exit_code, WNOHANG); + if (wait_res == -1) + ec = get_last_error(); + } + if (!ec && (wait_res == 0)) { handle.async_wait(std::move(self)); return ; diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index 1b45e11b3..44dc87055 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -55,7 +55,7 @@ test-suite standalone : ; test-suite with_target : - [ run process.cpp test_impl : : target ] - [ run windows.cpp test_impl : : target : no windows:yes windows:Advapi32 ] + [ run process.cpp test_impl : --log_level=all -- : target ] + [ run windows.cpp test_impl : --log_level=all -- : target : no windows:yes windows:Advapi32 ] ; diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 2249871d2..ac1d59eb4 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -101,12 +101,10 @@ BOOST_AUTO_TEST_CASE(exit_code_async) int called = 0; bpv::process proc1(ctx, pth, {"exit-code", "0"}); - bpv::process proc3(ctx, pth, {"exit-code", "2"}); bpv::process proc4(ctx, pth, {"exit-code", "42"}); bpv::process proc5(ctx, pth, {"sleep", "100"});; - proc1.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); bpv::async_execute( bpv::process(ctx, pth, {"exit-code", "1"}), From 8979836f324bdc7f47f3c6073651f0ea19e75513 Mon Sep 17 00:00:00 2001 From: Klemens Date: Mon, 6 Jun 2022 01:19:39 +0800 Subject: [PATCH 091/397] Added BOOST_TEST_IGNORE_SIGCHLD --- .../boost/process/v2/detail/process_handle_fd.hpp | 3 ++- .../v2/detail/process_handle_fd_or_signal.hpp | 7 +++---- .../process/v2/detail/process_handle_signal.hpp | 15 ++++++--------- test/v2/Jamfile.jam | 5 +++-- test/v2/process.cpp | 6 +++++- test/v2/test_impl.cpp | 2 +- 6 files changed, 20 insertions(+), 18 deletions(-) diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index 75f3eb965..7b10b13d4 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -208,7 +208,7 @@ struct basic_process_handle_fd else ec.clear(); - if (process_is_running(code)) + if (res == 0) return true; else { @@ -269,6 +269,7 @@ struct basic_process_handle_fd if (wait_res == -1) ec = get_last_error(); } + if (!ec && (wait_res == 0)) { 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 e427f5066..1e2ac0bcf 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 @@ -291,7 +291,7 @@ struct basic_process_handle_fd_or_signal ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; else { - wait_res = ::waitpid(pid_, &exit_code, WNOHANG); + wait_res = ::waitpid(pid_, &exit_code, WNOHANG); if (wait_res == -1) ec = get_last_error(); } @@ -326,9 +326,8 @@ struct basic_process_handle_fd_or_signal void operator()(Self &&self, error_code ec, int = 0) { native_exit_code_type exit_code{}; - if (!ec) - if (::waitpid(pid_, &exit_code, 0) == -1) - ec = get_last_error(); + if (!ec && ::waitpid(pid_, &exit_code, 0) == -1) + ec = get_last_error(); std::move(self).complete(ec, exit_code); } }; diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index f9785fc86..ab637ecab 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -202,10 +202,8 @@ struct basic_process_handle_signal int res = ::waitpid(pid_, &code, WNOHANG); if (res == -1) ec = get_last_error(); - else - ec.clear(); - - if (process_is_running(res)) + + if (res == 0) return true; else { @@ -254,8 +252,9 @@ struct basic_process_handle_signal void operator()(Self &&self) { error_code ec; - native_exit_code_type exit_code{}; + native_exit_code_type exit_code{-1}; int wait_res = -1; + if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; else @@ -282,15 +281,13 @@ struct basic_process_handle_signal }; BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(), completer{ec, exit_code, std::move(self)}); - } template - void operator()(Self &&self, error_code ec, int ) + void operator()(Self &&self, error_code ec, int sig) { native_exit_code_type exit_code{}; - if (!ec) - if (::waitpid(pid_, &exit_code, 0) == -1) + if (!ec && ::waitpid(pid_, &exit_code, 0) == -1) ec = get_last_error(); std::move(self).complete(ec, exit_code); } diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index 44dc87055..fb4575dea 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -42,6 +42,7 @@ lib header_test : header_1.cpp header_2.cpp : lib test_impl : test_impl.cpp filesystem : BOOST_PROCESS_V2_SEPARATE_COMPILATION=1 + BOOST_TEST_IGNORE_SIGCHLD=1 static windows:shell32 windows:user32 @@ -55,7 +56,7 @@ test-suite standalone : ; test-suite with_target : - [ run process.cpp test_impl : --log_level=all -- : target ] - [ run windows.cpp test_impl : --log_level=all -- : target : no windows:yes windows:Advapi32 ] + [ run process.cpp test_impl : --log_level=all --catch_system_errors=no -- : target ] + [ run windows.cpp test_impl : --log_level=all --catch_system_errors=no -- : target : no windows:yes windows:Advapi32 ] ; diff --git a/test/v2/process.cpp b/test/v2/process.cpp index ac1d59eb4..24924597c 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -12,7 +12,11 @@ #if defined(BOOST_FILESYSTEM_DYN_LINK) #undef BOOST_FILESYSTEM_DYN_LINK #endif +#define BOOST_TEST_IGNORE_SIGCHLD 1 +#if true //defined(BOOST_POSIX_API) +# include +#endif // Test that header file is self-contained. #include #include @@ -103,7 +107,7 @@ BOOST_AUTO_TEST_CASE(exit_code_async) bpv::process proc1(ctx, pth, {"exit-code", "0"}); bpv::process proc3(ctx, pth, {"exit-code", "2"}); bpv::process proc4(ctx, pth, {"exit-code", "42"}); - bpv::process proc5(ctx, pth, {"sleep", "100"});; + bpv::process proc5(ctx, pth, {"sleep", "100"}); proc1.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); bpv::async_execute( diff --git a/test/v2/test_impl.cpp b/test/v2/test_impl.cpp index bf27749fa..0cab70678 100644 --- a/test/v2/test_impl.cpp +++ b/test/v2/test_impl.cpp @@ -3,7 +3,7 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - +#define BOOST_TEST_IGNORE_SIGCHLD #define BOOST_TEST_MODULE process_v2_test #include From 686945f46fc79b8a91ea8803fe842753aab96432 Mon Sep 17 00:00:00 2001 From: Klemens Date: Mon, 6 Jun 2022 13:32:48 +0800 Subject: [PATCH 092/397] Fixed signal completion. --- .../v2/detail/process_handle_fd_or_signal.hpp | 26 +++++----- .../v2/detail/process_handle_signal.hpp | 48 +++++++++---------- .../process/v2/posix/default_launcher.hpp | 3 +- .../process/v2/posix/pdfork_launcher.hpp | 2 +- include/boost/process/v2/process.hpp | 18 ++++++- 5 files changed, 56 insertions(+), 41 deletions(-) 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 1e2ac0bcf..2e21f8490 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 @@ -19,12 +19,14 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) #include #include +#include #include #include #include #else #include #include +#include #include #include #include @@ -280,11 +282,11 @@ struct basic_process_handle_fd_or_signal BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_descriptor &descriptor; BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set &handle; pid_type pid_; + bool needs_post = true; template - void operator()(Self &&self) + void operator()(Self &&self, error_code ec = {}, int = 0) { - error_code ec; native_exit_code_type exit_code{}; int wait_res = -1; if (pid_ <= 0) // error, complete early @@ -298,9 +300,11 @@ struct basic_process_handle_fd_or_signal if (!ec && (wait_res == 0)) { + needs_post = false; if (descriptor.is_open()) descriptor.async_wait( - BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::descriptor_base::wait_read, std::move(self)); + BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::descriptor_base::wait_read, + std::move(self)); else handle.async_wait(std::move(self)); return; @@ -317,18 +321,14 @@ struct basic_process_handle_fd_or_signal self.complete(ec, code); } }; - BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(), - completer{ec, exit_code, std::move(self)}); - } + const auto exec = self.get_executor(); + completer cpl{ec, exit_code, std::move(self)}; + if (needs_post) + BOOST_PROCESS_V2_ASIO_NAMESPACE::post(exec, std::move(cpl)); + else + BOOST_PROCESS_V2_ASIO_NAMESPACE::dispatch(exec, std::move(cpl)); - template - void operator()(Self &&self, error_code ec, int = 0) - { - native_exit_code_type exit_code{}; - if (!ec && ::waitpid(pid_, &exit_code, 0) == -1) - ec = get_last_error(); - std::move(self).complete(ec, exit_code); } }; }; diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index ab637ecab..7f58c1e92 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -18,11 +18,13 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) #include #include +#include #include #include #else #include #include +#include #include #include #endif @@ -71,7 +73,7 @@ struct basic_process_handle_signal } basic_process_handle_signal(Executor executor, pid_type pid) - : pid_(pid), signal_set_(executor, SIGCHLD) + : pid_(pid), signal_set_(executor, SIGCHLD) { } @@ -84,7 +86,9 @@ struct basic_process_handle_signal basic_process_handle_signal& operator=(basic_process_handle_signal && handle) { pid_ = handle.id(); - //signal_set_ = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set(handle.get_executor(), SIGCHLD); + signal_set_.~basic_signal_set(); + using ss = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set; + new (&signal_set_) ss(handle.get_executor(), SIGCHLD); handle.pid_ = -1; return *this; } @@ -102,13 +106,7 @@ struct basic_process_handle_signal void terminate_if_running(error_code &) { - if (pid_ <= 0) - return ; - if (::waitpid(pid_, nullptr, WNOHANG) == 0) - { - ::kill(pid_, SIGKILL); - ::waitpid(pid_, nullptr, 0); - } + terminate_if_running(); } void terminate_if_running() @@ -144,7 +142,7 @@ struct basic_process_handle_signal { if (pid_ <= 0) return ; - if (::kill(pid_, SIGINT) == -1) + if (::kill(pid_, SIGTERM) == -1) ec = get_last_error(); } @@ -246,25 +244,28 @@ struct basic_process_handle_signal struct async_wait_op_ { - BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set &handle; + BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set &handle; pid_type pid_; + bool needs_post = true; + template - void operator()(Self &&self) + void operator()(Self &&self, error_code ec = {}, int = 0) { - error_code ec; - native_exit_code_type exit_code{-1}; + native_exit_code_type exit_code = -1; int wait_res = -1; if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; - else + else if (!ec) { wait_res = ::waitpid(pid_, &exit_code, WNOHANG); if (wait_res == -1) ec = get_last_error(); } + if (!ec && (wait_res == 0)) { + needs_post = false; handle.async_wait(std::move(self)); return ; } @@ -274,22 +275,19 @@ struct basic_process_handle_signal error_code ec; native_exit_code_type code; typename std::decay::type self; + void operator()() { self.complete(ec, code); } }; - BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(), - completer{ec, exit_code, std::move(self)}); - } - template - void operator()(Self &&self, error_code ec, int sig) - { - native_exit_code_type exit_code{}; - if (!ec && ::waitpid(pid_, &exit_code, 0) == -1) - ec = get_last_error(); - std::move(self).complete(ec, exit_code); + const auto exec = self.get_executor(); + completer cpl{ec, exit_code, std::move(self)}; + if (needs_post) + BOOST_PROCESS_V2_ASIO_NAMESPACE::post(exec, std::move(cpl)); + else + BOOST_PROCESS_V2_ASIO_NAMESPACE::dispatch(exec, std::move(cpl)); } }; }; diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index fef016c6f..2445426ef 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -404,7 +404,7 @@ struct default_launcher if (!ec) ::execve(executable.c_str(), const_cast(argv), const_cast(env)); - ::write(pg.p[1], &errno, sizeof(int)); + ignore_unused(::write(pg.p[1], &errno, sizeof(int))); ec.assign(errno, system_category()); detail::on_exec_error(*this, executable, argv, ec, inits...); ::exit(EXIT_FAILURE); @@ -440,6 +440,7 @@ struct default_launcher } protected: + void ignore_unused(std::size_t ) {} void close_all_fds(error_code & ec) { std::sort(fd_whitelist.begin(), fd_whitelist.end()); diff --git a/include/boost/process/v2/posix/pdfork_launcher.hpp b/include/boost/process/v2/posix/pdfork_launcher.hpp index 329ecc10a..fe699a7e0 100644 --- a/include/boost/process/v2/posix/pdfork_launcher.hpp +++ b/include/boost/process/v2/posix/pdfork_launcher.hpp @@ -126,7 +126,7 @@ struct pdfork_launcher : default_launcher if (!ec) ::execve(executable.c_str(), const_cast(argv), const_cast(env)); - ::write(pg.p[1], &errno, sizeof(int)); + default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int))); ec.assign(errno, system_category()); detail::on_exec_error(*this, executable, argv, ec, inits...); ::exit(EXIT_FAILURE); diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index f1f03e53b..7acd4d327 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -326,7 +326,23 @@ struct basic_process template void operator()(Self && self) { - handle.async_wait(std::move(self)); + if (!process_is_running(res)) + { + struct completer + { + int code; + typename std::decay::type self; + void operator()() + { + self.complete(error_code{}, evaluate_exit_code(code)); + } + }; + + BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(), + completer{res, std::move(self)}); + } + else + handle.async_wait(std::move(self)); } template From 442a6ed8d8fa7c72692673adb45ec6c32a9be3fb Mon Sep 17 00:00:00 2001 From: Klemens Date: Mon, 6 Jun 2022 23:48:35 +0800 Subject: [PATCH 093/397] Fixed fork_parent --- .../v2/detail/process_handle_signal.hpp | 23 ++++++++----- .../process/v2/posix/default_launcher.hpp | 6 ++-- .../v2/posix/fork_and_forget_launcher.hpp | 1 + .../process/v2/posix/pdfork_launcher.hpp | 3 +- .../boost/process/v2/posix/vfork_launcher.hpp | 3 +- test/v2/process.cpp | 33 ++++++++++++------- test/v2/target.cpp | 2 ++ 7 files changed, 48 insertions(+), 23 deletions(-) diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index 7f58c1e92..89174a221 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -246,11 +246,23 @@ struct basic_process_handle_signal { BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set &handle; pid_type pid_; - bool needs_post = true; + + template + void operator()(Self &&self) + { + handle.async_wait(std::move(self)); + handle.cancel(); + // we cancel so we end up on the signal-sets executor + } template - void operator()(Self &&self, error_code ec = {}, int = 0) + void operator()(Self &&self, error_code ec, int sig) { + if (ec == BOOST_PROCESS_V2_ASIO_NAMESPACE::error::operation_aborted && + self.get_cancellation_state().cancelled() + == BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_type::none) + ec.clear(); + native_exit_code_type exit_code = -1; int wait_res = -1; @@ -265,7 +277,6 @@ struct basic_process_handle_signal if (!ec && (wait_res == 0)) { - needs_post = false; handle.async_wait(std::move(self)); return ; } @@ -283,11 +294,7 @@ struct basic_process_handle_signal }; const auto exec = self.get_executor(); - completer cpl{ec, exit_code, std::move(self)}; - if (needs_post) - BOOST_PROCESS_V2_ASIO_NAMESPACE::post(exec, std::move(cpl)); - else - BOOST_PROCESS_V2_ASIO_NAMESPACE::dispatch(exec, std::move(cpl)); + BOOST_PROCESS_V2_ASIO_NAMESPACE::dispatch(exec, completer{ec, exit_code, std::move(self)}); } }; }; diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index 2445426ef..5385726dc 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -385,6 +385,7 @@ struct default_launcher pid = ::fork(); if (pid == -1) { + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent); detail::on_fork_error(*this, executable, argv, ec, inits...); detail::on_error(*this, executable, argv, ec, inits...); @@ -393,13 +394,13 @@ struct default_launcher } else if (pid == 0) { - ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); ::close(pg.p[0]); + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child); ec = detail::on_exec_setup(*this, executable, argv, inits...); if (!ec) { fd_whitelist.push_back(pg.p[1]); - //close_all_fds(ec); + close_all_fds(ec); } if (!ec) ::execve(executable.c_str(), const_cast(argv), const_cast(env)); @@ -411,6 +412,7 @@ struct default_launcher return basic_process{exec}; } + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent); ::close(pg.p[1]); pg.p[1] = -1; int child_error{0}; diff --git a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp index 301746d96..1b92b5d7e 100644 --- a/include/boost/process/v2/posix/fork_and_forget_launcher.hpp +++ b/include/boost/process/v2/posix/fork_and_forget_launcher.hpp @@ -90,6 +90,7 @@ struct fork_and_forget_launcher : default_launcher pid = ::fork(); if (pid == -1) { + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent); detail::on_fork_error(*this, executable, argv, ec, inits...); detail::on_error(*this, executable, argv, ec, inits...); diff --git a/include/boost/process/v2/posix/pdfork_launcher.hpp b/include/boost/process/v2/posix/pdfork_launcher.hpp index fe699a7e0..98985995e 100644 --- a/include/boost/process/v2/posix/pdfork_launcher.hpp +++ b/include/boost/process/v2/posix/pdfork_launcher.hpp @@ -106,6 +106,7 @@ struct pdfork_launcher : default_launcher pid = ::pdfork(&fd, PD_DAEMON | PD_CLOEXEC); if (pid == -1) { + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent); detail::on_fork_error(*this, executable, argv, ec, inits...); detail::on_error(*this, executable, argv, ec, inits...); @@ -132,7 +133,7 @@ struct pdfork_launcher : default_launcher ::exit(EXIT_FAILURE); return basic_process{exec}; } - + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent); ::close(pg.p[1]); pg.p[1] = -1; int child_error{0}; diff --git a/include/boost/process/v2/posix/vfork_launcher.hpp b/include/boost/process/v2/posix/vfork_launcher.hpp index 06892be59..fde3842a5 100644 --- a/include/boost/process/v2/posix/vfork_launcher.hpp +++ b/include/boost/process/v2/posix/vfork_launcher.hpp @@ -92,6 +92,7 @@ struct vfork_launcher : default_launcher pid = ::vfork(); if (pid == -1) { + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent); detail::on_fork_error(*this, executable, argv, ec, inits...); detail::on_error(*this, executable, argv, ec, inits...); @@ -112,7 +113,7 @@ struct vfork_launcher : default_launcher ::exit(EXIT_FAILURE); return basic_process{exec}; } - ctx.notify_fork(asio::execution_context::fork_parent); + ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent); if (ec) { diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 24924597c..f29404de9 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -103,25 +103,36 @@ BOOST_AUTO_TEST_CASE(exit_code_async) boost::asio::io_context ctx; int called = 0; + bpv::process proc1(ctx, pth, {"exit-code", "0"}); bpv::process proc3(ctx, pth, {"exit-code", "2"}); bpv::process proc4(ctx, pth, {"exit-code", "42"}); bpv::process proc5(ctx, pth, {"sleep", "100"}); + bpv::process proc6(ctx, pth, {"sleep", "50"}); + +#define CPL(Code) \ + [&](bpv::error_code ec, int e) \ + { \ + BOOST_CHECK_MESSAGE(!ec, ec.message()); \ + called++; \ + BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), Code); \ + } + + proc1.async_wait(CPL(0)); + proc3.async_wait(CPL(2)); + proc4.async_wait(CPL(42)); + proc5.async_wait(CPL(0)); + proc6.async_wait(CPL(0)); + bpv::async_execute(bpv::process(ctx, pth, {"exit-code", "1"}),CPL(1)); + bpv::async_execute(bpv::process(ctx, pth, {"sleep", "100"}), CPL(0)); + +#undef CPL - proc1.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); - bpv::async_execute( - bpv::process(ctx, pth, {"exit-code", "1"}), - [&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 1);}); - proc3.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 2);}); - proc4.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 42);}); - proc5.async_wait([&](bpv::error_code ec, int e) {BOOST_CHECK_MESSAGE(!ec, ec.message()); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); - bpv::async_execute( - bpv::process(ctx, pth, {"sleep", "100"}), - [&](bpv::error_code ec, int e) {BOOST_CHECK(!ec); called++; BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), 0);}); + //signal(SIGCHLD, [](int){printf("SIGCHLD\n");}); ctx.run(); - BOOST_CHECK_EQUAL(called, 6); + BOOST_CHECK_EQUAL(called, 7); } diff --git a/test/v2/target.cpp b/test/v2/target.cpp index 4449da007..d095c4a36 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -16,7 +16,9 @@ int main(int argc, char * argv[]) { std::string mode = argv[1]; if (mode == "exit-code") + { return std::stoi(argv[2]); + } else if (mode == "sleep") { const auto delay = std::chrono::milliseconds(std::stoi(argv[2])); From 9d006cdd9472c1dc5593275338b5807483f84b5d Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 7 Jun 2022 00:19:34 +0800 Subject: [PATCH 094/397] Improved environment tests to not drop other vars --- test/v2/process.cpp | 15 ++++++++++++--- test/v2/target.cpp | 2 -- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/test/v2/process.cpp b/test/v2/process.cpp index f29404de9..0f8d0c26e 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -437,11 +437,20 @@ BOOST_AUTO_TEST_CASE(environment) std::string path = ::getenv("PATH"); BOOST_CHECK_EQUAL(read_env("PATH"), ::getenv("PATH")); - path = "PATH=" + path; - BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{"FOOBAR=FOO-BAR", path.c_str()})); + auto c = bpv::environment::current(); + std::vector sub_env(c.begin(), c.end()); + + sub_env.push_back("FOOBAR=FOO-BAR"); + BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{sub_env})); + + sub_env.push_back("XYZ=ZYX"); + auto itr = std::find_if(sub_env.begin(), sub_env.end(), [](const bpv::environment::key_value_pair & kv) {return kv.key() == "PATH";}); path += static_cast(bpv::environment::delimiter); path += "/bar/foo"; - BOOST_CHECK_EQUAL(path.substr(5), read_env("PATH", bpv::process_environment{path.c_str(), "XYZ=ZYX"})); + bpv::environment::value pval = itr->value(); + pval.push_back("/bar/foo"); + *itr = bpv::environment::key_value_pair("PATH", pval); + BOOST_CHECK_EQUAL(path, read_env("PATH", bpv::process_environment{sub_env})); #if defined(BOOST_PROCESS_V2_WINDOWS) std::wstring wpath = L"PATh=" + std::wstring(_wgetenv(L"PatH")); diff --git a/test/v2/target.cpp b/test/v2/target.cpp index d095c4a36..4449da007 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -16,9 +16,7 @@ int main(int argc, char * argv[]) { std::string mode = argv[1]; if (mode == "exit-code") - { return std::stoi(argv[2]); - } else if (mode == "sleep") { const auto delay = std::chrono::milliseconds(std::stoi(argv[2])); From dd4bf8d8578d3e85fbd3949b3f9d79befa0453a5 Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 7 Jun 2022 00:56:03 +0800 Subject: [PATCH 095/397] Debugging env --- test/v2/process.cpp | 3 ++- test/v2/target.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/v2/process.cpp b/test/v2/process.cpp index 0f8d0c26e..af6163814 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -87,6 +87,7 @@ BOOST_AUTO_TEST_CASE(exit_code_sync) args[1] = "42"; auto proc = bpv::default_process_launcher()(ctx, pth, args); BOOST_CHECK_EQUAL(proc.wait(), 42); + printf("42: %d\n", proc.native_exit_code()); BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"sleep", "100"}).wait(), 0); BOOST_CHECK_EQUAL(bpv::execute(bpv::process(ctx, pth, {"sleep", "100"})), 0); @@ -424,7 +425,7 @@ std::string read_env(const char * name, Inits && ... inits) BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); out.resize(sz); trim_end(out); - printf("Read env %s: '%s'\n", name, out.c_str()); + printf("Read env (%ld) %s: '%s'\n", sz, name, out.c_str()); proc.wait(); BOOST_CHECK_EQUAL(proc.exit_code(), 0); diff --git a/test/v2/target.cpp b/test/v2/target.cpp index 4449da007..806bac352 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -59,10 +59,10 @@ int main(int argc, char * argv[]) assert(printf("%s", p) > 0); else { - printf("Can't find %s in environment\n", argv[2]); + assert(printf("Can't find %s in environment\n", argv[2]) > 0); for (auto e = environ; e != nullptr; e++) - printf(" %s\n", *e); - return 1; + assert(printf(" %s\n", *e) > 0); + return 3; } } #if defined(BOOST_PROCESS_V2_WINDOWS) From 727881649c13e6a0e854e952d3583030bb76cbcd Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 7 Jun 2022 10:17:23 +0800 Subject: [PATCH 096/397] Typo fixes --- .drone.star | 2 +- include/boost/process/v2/detail/process_handle_fd.hpp | 2 +- include/boost/process/v2/detail/process_handle_fd_or_signal.hpp | 2 +- include/boost/process/v2/detail/process_handle_signal.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone.star b/.drone.star index a62e69482..44e54b758 100644 --- a/.drone.star +++ b/.drone.star @@ -25,7 +25,7 @@ def main(ctx): linux_cxx("Default clang++ with libc++", "clang++-libc++", packages="libc++-dev mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "B2_TOOLSET": "clang-7", "B2_CXXSTD": "11", "VARIANT": "debug", "TOOLSET": "clang", "COMPILER": "clang++-libc++", "CXXSTD": "11", "CXX_FLAGS": "-stdlib=libc++ -stdlib=libc++", "TRAVISCLANG" : "yes" }, globalenv=globalenv), linux_cxx("Default g++", "g++", packages="mlocate", image="cppalliance/droneubuntu1604:1", buildtype="buildtype", buildscript="drone", environment={ "VARIANT": "release", "TOOLSET": "gcc", "COMPILER": "g++", "CXXSTD": "11" }, globalenv=globalenv), linux_cxx("Clang 3.8, UBasan", "clang++-3.8", packages="clang-3.8 libssl-dev mlocate", llvm_os="precise", llvm_ver="3.8", image="cppalliance/droneubuntu1604:1", buildtype="boost", buildscript="drone", environment={"VARIANT": "process_ubasan", "TOOLSET": "clang", "COMPILER": "clang++-3.8", "CXXSTD": "11", "UBSAN_OPTIONS": 'print_stacktrace=1', "DRONE_BEFORE_INSTALL": "UBasan" }, globalenv=globalenv), - linux_cxx("gcc 5", "g++-5", packages="g++-5", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-5', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), + linux_cxx("gcc 6", "g++-6", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-6', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv), linux_cxx("clang 3.8", "clang++-3.8", packages="clang-3.8", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu1604:1", environment={'B2_TOOLSET': 'clang', 'COMPILER': 'clang++-3.8', 'B2_CXXSTD': '11', 'DRONE_JOB_UUID': '7b52009b64'}, globalenv=globalenv), osx_cxx("clang", "g++", packages="", buildtype="boost", buildscript="drone", environment={'B2_TOOLSET': 'clang', 'B2_CXXSTD': '11,17', 'DRONE_JOB_UUID': '91032ad7bb'}, globalenv=globalenv), linux_cxx("coverity", "g++", packages="", buildtype="coverity", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'Coverity Scan', 'B2_TOOLSET': 'clang', 'DRONE_JOB_UUID': '472b07b9fc'}, globalenv=globalenv), diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index 7b10b13d4..59b3ae1d8 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -127,7 +127,7 @@ struct basic_process_handle_fd { if (pid_ <= 0) return; - if (::waitpid(pid_, &exit_status, 0) == 1) + if (::waitpid(pid_, &exit_status, 0) < 0) ec = get_last_error(); } 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 2e21f8490..734af9d25 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 @@ -155,7 +155,7 @@ struct basic_process_handle_fd_or_signal { if (pid_ <= 0) return; - if (::waitpid(pid_, &exit_status, 0) == 1) + if (::waitpid(pid_, &exit_status, 0) < 0) ec = get_last_error(); } diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index 89174a221..54ab9d686 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -124,7 +124,7 @@ struct basic_process_handle_signal { if (pid_ <= 0) return ; - if (::waitpid(pid_, &exit_status, 0) == 1) + if (::waitpid(pid_, &exit_status, 0) < 0) ec = get_last_error(); } From 618c93118885a69040a245cd821b8ae08a549920 Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 7 Jun 2022 11:16:09 +0800 Subject: [PATCH 097/397] Added more additional diagnostics to test --- test/v2/process.cpp | 13 ++++++------- test/v2/target.cpp | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/test/v2/process.cpp b/test/v2/process.cpp index af6163814..c20f3cdc6 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -77,7 +77,6 @@ BOOST_AUTO_TEST_CASE(exit_code_sync) using boost::unit_test::framework::master_test_suite; const auto pth = master_test_suite().argv[1]; - bpv::environment::set("BOOST_PROCESS_V2_TEST_SUBPROCESS", "test"); boost::asio::io_context ctx; BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"exit-code", "0"}).wait(), 0); @@ -91,20 +90,18 @@ BOOST_AUTO_TEST_CASE(exit_code_sync) BOOST_CHECK_EQUAL(bpv::process(ctx, pth, {"sleep", "100"}).wait(), 0); BOOST_CHECK_EQUAL(bpv::execute(bpv::process(ctx, pth, {"sleep", "100"})), 0); - - } BOOST_AUTO_TEST_CASE(exit_code_async) { using boost::unit_test::framework::master_test_suite; const auto pth = master_test_suite().argv[1]; - - bpv::environment::set("BOOST_PROCESS_V2_TEST_SUBPROCESS", "test"); + printf("Executing '%s'\n", pth); + boost::asio::io_context ctx; int called = 0; - + printf("Setting up processes\n"); bpv::process proc1(ctx, pth, {"exit-code", "0"}); bpv::process proc3(ctx, pth, {"exit-code", "2"}); @@ -120,6 +117,8 @@ BOOST_AUTO_TEST_CASE(exit_code_async) BOOST_CHECK_EQUAL(bpv::evaluate_exit_code(e), Code); \ } + printf("Waiting for processes\n"); + proc1.async_wait(CPL(0)); proc3.async_wait(CPL(2)); proc4.async_wait(CPL(42)); @@ -130,7 +129,7 @@ BOOST_AUTO_TEST_CASE(exit_code_async) #undef CPL - //signal(SIGCHLD, [](int){printf("SIGCHLD\n");}); + printf("Running\n"); ctx.run(); BOOST_CHECK_EQUAL(called, 7); diff --git a/test/v2/target.cpp b/test/v2/target.cpp index 806bac352..a56fbcb24 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -14,7 +14,20 @@ extern char **environ; int main(int argc, char * argv[]) { + if (argc < 2) + return 30; + std::string mode = argv[1]; + + if (mode != "print-args") + { + fprintf(stderr, "Target process starting: "); + for (int i = 0; i < argc; i++) + fprintf(stderr, " '%s' ", argv[i]); + fprintf(stderr, "\n"); + + } + if (mode == "exit-code") return std::stoi(argv[2]); else if (mode == "sleep") @@ -30,7 +43,6 @@ int main(int argc, char * argv[]) std::cerr << argv[i] << std::endl; if (!std::cout || !std::cerr) return 1; - } else if (mode == "echo") std::cout << std::cin.rdbuf(); @@ -42,7 +54,7 @@ int main(int argc, char * argv[]) std::wcout << boost::process::v2::wstring_view(buf, sz) << std::flush; #else char buf[65535]; - printf(::getcwd(buf, sizeof(buf))); + assert(printf(::getcwd(buf, sizeof(buf))) > 0); #endif return 0; } From f59c1c180eff585e41ae728131ea2afcdeef39cd Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 7 Jun 2022 11:38:03 +0800 Subject: [PATCH 098/397] Removed asserts around printf. --- test/v2/process.cpp | 2 ++ test/v2/target.cpp | 21 ++++----------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/test/v2/process.cpp b/test/v2/process.cpp index c20f3cdc6..ce635c8ce 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -95,6 +95,8 @@ BOOST_AUTO_TEST_CASE(exit_code_sync) BOOST_AUTO_TEST_CASE(exit_code_async) { using boost::unit_test::framework::master_test_suite; + printf("Args: '%d'\n", master_test_suite().argc); + printf("Exe '%s'\n", master_test_suite().argv[0]); const auto pth = master_test_suite().argv[1]; printf("Executing '%s'\n", pth); diff --git a/test/v2/target.cpp b/test/v2/target.cpp index a56fbcb24..c4ccdab68 100644 --- a/test/v2/target.cpp +++ b/test/v2/target.cpp @@ -14,20 +14,7 @@ extern char **environ; int main(int argc, char * argv[]) { - if (argc < 2) - return 30; - std::string mode = argv[1]; - - if (mode != "print-args") - { - fprintf(stderr, "Target process starting: "); - for (int i = 0; i < argc; i++) - fprintf(stderr, " '%s' ", argv[i]); - fprintf(stderr, "\n"); - - } - if (mode == "exit-code") return std::stoi(argv[2]); else if (mode == "sleep") @@ -54,7 +41,7 @@ int main(int argc, char * argv[]) std::wcout << boost::process::v2::wstring_view(buf, sz) << std::flush; #else char buf[65535]; - assert(printf(::getcwd(buf, sizeof(buf))) > 0); + printf(::getcwd(buf, sizeof(buf))); #endif return 0; } @@ -68,12 +55,12 @@ int main(int argc, char * argv[]) { auto p = ::getenv(argv[2]); if (p && *p) - assert(printf("%s", p) > 0); + printf("%s", p); else { - assert(printf("Can't find %s in environment\n", argv[2]) > 0); + printf("Can't find %s in environment\n", argv[2]); for (auto e = environ; e != nullptr; e++) - assert(printf(" %s\n", *e) > 0); + printf(" %s\n", *e); return 3; } } From 4d59330067699faa6094ad652d79ce9e62b7e1dd Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 7 Jun 2022 12:06:33 +0800 Subject: [PATCH 099/397] Added EINTR handling for OSX --- .../boost/process/v2/detail/process_handle_fd.hpp | 10 ++++++++-- .../v2/detail/process_handle_fd_or_signal.hpp | 13 ++++++++++--- .../process/v2/detail/process_handle_signal.hpp | 10 ++++++++-- test/v2/process.cpp | 3 +++ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/include/boost/process/v2/detail/process_handle_fd.hpp b/include/boost/process/v2/detail/process_handle_fd.hpp index 59b3ae1d8..868542661 100644 --- a/include/boost/process/v2/detail/process_handle_fd.hpp +++ b/include/boost/process/v2/detail/process_handle_fd.hpp @@ -127,8 +127,14 @@ struct basic_process_handle_fd { if (pid_ <= 0) return; - if (::waitpid(pid_, &exit_status, 0) < 0) - ec = get_last_error(); + while (::waitpid(pid_, &exit_status, 0) < 0) + { + if (errno != EINTR) + { + ec = get_last_error(); + break; + } + } } void wait(native_exit_code_type &exit_status) 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 734af9d25..c91b31535 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 @@ -155,8 +155,15 @@ struct basic_process_handle_fd_or_signal { if (pid_ <= 0) return; - if (::waitpid(pid_, &exit_status, 0) < 0) - ec = get_last_error(); + while (::waitpid(pid_, &exit_status, 0) < 0) + { + if (errno != EINTR) + { + ec = get_last_error(); + break; + } + } + } void wait(native_exit_code_type &exit_status) @@ -228,7 +235,7 @@ struct basic_process_handle_fd_or_signal if (pid_ <= 0) return false; int code = 0; - int res = ::waitpid(pid_, &code, 0); + int res = ::waitpid(pid_, &code, WNOHANG); if (res == -1) ec = get_last_error(); else diff --git a/include/boost/process/v2/detail/process_handle_signal.hpp b/include/boost/process/v2/detail/process_handle_signal.hpp index 54ab9d686..cf042c40d 100644 --- a/include/boost/process/v2/detail/process_handle_signal.hpp +++ b/include/boost/process/v2/detail/process_handle_signal.hpp @@ -124,8 +124,14 @@ struct basic_process_handle_signal { if (pid_ <= 0) return ; - if (::waitpid(pid_, &exit_status, 0) < 0) - ec = get_last_error(); + while (::waitpid(pid_, &exit_status, 0) < 0) + { + if (errno != EINTR) + { + ec = get_last_error(); + break; + } + } } void wait(native_exit_code_type &exit_status) diff --git a/test/v2/process.cpp b/test/v2/process.cpp index ce635c8ce..d160b5dfa 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -95,6 +95,9 @@ BOOST_AUTO_TEST_CASE(exit_code_sync) BOOST_AUTO_TEST_CASE(exit_code_async) { using boost::unit_test::framework::master_test_suite; + printf("Running exit_code_async\n"); + auto & mm = master_test_suite(); + printf("Running exit_code_async %p\n", &mm); printf("Args: '%d'\n", master_test_suite().argc); printf("Exe '%s'\n", master_test_suite().argv[0]); const auto pth = master_test_suite().argv[1]; From 43e845a691cf95f228982043066025d6ccee1123 Mon Sep 17 00:00:00 2001 From: Klemens Date: Wed, 8 Jun 2022 14:58:06 +0800 Subject: [PATCH 100/397] Fixed execute_op error --- include/boost/process/v2/execute.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/boost/process/v2/execute.hpp b/include/boost/process/v2/execute.hpp index 6d0b1d4f2..10a203a24 100644 --- a/include/boost/process/v2/execute.hpp +++ b/include/boost/process/v2/execute.hpp @@ -71,7 +71,8 @@ struct execute_op if (s.is_connected()) s.emplace(proc.get()); - proc->async_wait( + auto pro_ = proc.get(); + pro_->async_wait( BOOST_PROCESS_V2_ASIO_NAMESPACE::bind_cancellation_slot( BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_slot(), std::move(self))); @@ -79,7 +80,7 @@ struct execute_op template void operator()(Self && self, error_code ec, int res) - { + { self.get_cancellation_state().slot().clear(); self.complete(ec, res); } From 26f4584e1e9f75550fee22bcd2367c00b06c9ffe Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 10 Jun 2022 19:52:41 +0800 Subject: [PATCH 101/397] Increased wait time for group_wait --- test/group_wait.cpp | 2 +- test/limit_fd.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/group_wait.cpp b/test/group_wait.cpp index 142645f68..f57707051 100644 --- a/test/group_wait.cpp +++ b/test/group_wait.cpp @@ -103,7 +103,7 @@ BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15)) bp::child c1( master_test_suite().argv[1], - "--wait", "1", + "--wait", "2", g, ec ); diff --git a/test/limit_fd.cpp b/test/limit_fd.cpp index fa33ee807..aa391756b 100644 --- a/test/limit_fd.cpp +++ b/test/limit_fd.cpp @@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(iterate_handles, *boost::unit_test::timeout(5)) BOOST_CHECK_MESSAGE(!ec, ec.message()); - BOOST_CHECK_EQUAL(ret, 42u); + BOOST_CHECK_EQUAL(ret, 42); BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0u); BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0u); } From 69a06155303bfe399e75d112951bc5168d2f7311 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 10 Jun 2022 22:04:01 +0800 Subject: [PATCH 102/397] Fixed times in test --- test/group_wait.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/group_wait.cpp b/test/group_wait.cpp index f57707051..9d911fe5d 100644 --- a/test/group_wait.cpp +++ b/test/group_wait.cpp @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(wait_group_test_timeout, *boost::unit_test::timeout(15)) BOOST_REQUIRE(c1.in_group()); BOOST_REQUIRE(c2.in_group()); - BOOST_CHECK(!g.wait_for(std::chrono::seconds(2), ec)); + BOOST_CHECK(!g.wait_for(std::chrono::milliseconds(2500), ec)); BOOST_CHECK_MESSAGE(!ec, std::to_string(ec.value()) + " == " + ec.message()); BOOST_CHECK(!c1.running()); From e585864cf4138adb85a6c4276c9893f3953f68ad Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 11 Jun 2022 10:45:14 +0800 Subject: [PATCH 103/397] Changed default whitelist to stdio --- include/boost/process/v2/posix/default_launcher.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index 5385726dc..0486f0696 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -297,7 +297,7 @@ struct default_launcher int pid = -1; /// The whitelist for file descriptors. - std::vector fd_whitelist; + std::vector fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}; default_launcher() = default; @@ -447,7 +447,7 @@ struct default_launcher { std::sort(fd_whitelist.begin(), fd_whitelist.end()); detail::close_all(fd_whitelist, ec); - fd_whitelist.clear(); + fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}; } struct pipe_guard From c1fb7758b2d0dba2f531dfa3b44811885ce0be40 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 11 Jun 2022 13:25:27 +0800 Subject: [PATCH 104/397] Added missing incldue --- include/boost/process/detail/posix/handles.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/boost/process/detail/posix/handles.hpp b/include/boost/process/detail/posix/handles.hpp index 1572f0593..cd9e1ce5a 100644 --- a/include/boost/process/detail/posix/handles.hpp +++ b/include/boost/process/detail/posix/handles.hpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include namespace boost { namespace process { namespace detail { namespace posix { From 1a1d677d769f4016422c0b26de971f43ec91a20f Mon Sep 17 00:00:00 2001 From: Klemens Date: Sat, 11 Jun 2022 15:00:32 +0800 Subject: [PATCH 105/397] Closes #202 --- include/boost/process/detail/posix/executor.hpp | 8 ++++++++ include/boost/process/detail/used_handles.hpp | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index 930e0c546..8b9328c3e 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -151,6 +151,7 @@ class executor int _pipe_sink = -1; + void write_error(const std::error_code & ec, const char * msg) { //I am the child @@ -327,6 +328,13 @@ class executor } void set_error(const std::error_code &ec, const std::string &msg) {set_error(ec, msg.c_str());}; + std::vector get_used_handles() const + { + if (_pipe_sink == -1) + return {}; + else + return {_pipe_sink}; + }; }; template diff --git a/include/boost/process/detail/used_handles.hpp b/include/boost/process/detail/used_handles.hpp index 4d56af357..8db226d43 100644 --- a/include/boost/process/detail/used_handles.hpp +++ b/include/boost/process/detail/used_handles.hpp @@ -69,7 +69,7 @@ template std::vector<::boost::process::detail::api::native_handle_type> get_used_handles(Executor &exec) { - std::vector<::boost::process::detail::api::native_handle_type> res; + std::vector<::boost::process::detail::api::native_handle_type> res = exec.get_used_handles(); foreach_used_handle(exec, [&](::boost::process::detail::api::native_handle_type handle){res.push_back(handle);}); return res; } From a7b65bfc44f4af3a9d584fe9347fde0944c0167a Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Tue, 2 Aug 2022 09:36:44 +0100 Subject: [PATCH 106/397] Fix compiling for MinGW-w64 using std::filesystem --- include/boost/process/detail/windows/basic_cmd.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/boost/process/detail/windows/basic_cmd.hpp b/include/boost/process/detail/windows/basic_cmd.hpp index 53ea9931d..760b28ba0 100644 --- a/include/boost/process/detail/windows/basic_cmd.hpp +++ b/include/boost/process/detail/windows/basic_cmd.hpp @@ -159,8 +159,13 @@ struct exe_cmd_init : handler_base_ext return exe_cmd_init(std::move(sh), std::move(args_)); } +#ifdef BOOST_PROCESS_USE_STD_FS + static std:: string get_shell(char) {return shell(). string(); } + static std::wstring get_shell(wchar_t) {return shell().wstring(); } +#else static std:: string get_shell(char) {return shell(). string(codecvt()); } static std::wstring get_shell(wchar_t) {return shell().wstring(codecvt());} +#endif static exe_cmd_init cmd_shell(string_type&& cmd) { From 317b1b7c62e2b00fa231db43d4b00715bf578092 Mon Sep 17 00:00:00 2001 From: Klemens Date: Wed, 17 Aug 2022 18:12:55 +0800 Subject: [PATCH 107/397] Added Boost::process to link_libraries in CMake. --- test/CMakeLists.txt | 10 +++++----- test/v2/CMakeLists.txt | 11 +++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7867c8adf..7ffb08113 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,16 +2,16 @@ enable_testing() add_executable(boost_process_sparring_partner sparring_partner.cpp ) -target_link_libraries(boost_process_sparring_partner Boost::program_options Boost::filesystem Boost::iostreams) +target_link_libraries(boost_process_sparring_partner Boost::process Boost::program_options Boost::filesystem Boost::iostreams) add_executable(boost_process_exit_argc exit_argc.cpp) add_executable(boost_process_sub_launch sub_launcher.cpp) -target_link_libraries(boost_process_sub_launch Boost::program_options Boost::filesystem Boost::iostreams Boost::system) +target_link_libraries(boost_process_sub_launch Boost::process Boost::program_options Boost::filesystem Boost::iostreams Boost::system) function(process_standalone_test name ) add_executable(boost_process_${name} ${name}.cpp) - target_link_libraries(boost_process_${name} Boost::system Boost::filesystem) + target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem) add_test(NAME boost_process_${name} COMMAND $ ) endfunction() @@ -21,7 +21,7 @@ process_standalone_test(pipe) function(process_sub_launch_test name ) add_executable(boost_process_${name} ${name}.cpp) - target_link_libraries(boost_process_${name} Boost::system Boost::filesystem Boost::thread) + target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread) add_test(NAME boost_process_${name} COMMAND $ $ ) endfunction() @@ -30,7 +30,7 @@ process_sub_launch_test(group_wait) function(process_sparring_partner_launch name ) add_executable(boost_process_${name} ${name}.cpp) - target_link_libraries(boost_process_${name} Boost::system Boost::filesystem Boost::thread) + target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread) add_test(NAME boost_process_${name} COMMAND $ $ ) endfunction() diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index 937c78566..247afdabc 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -1,13 +1,13 @@ enable_testing() add_library(boost_process_v2_test_impl test_impl.cpp) -target_link_libraries(boost_process_v2_test_impl Boost::unit_test_framework Boost::process) +target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process) target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1) -target_include_directories(boost_process_v2_test_impl PUBLIC ../../include) + function(boost_process_v2_standalone_test name) add_executable(boost_process_v2_${name} ${name}.cpp) - target_link_libraries(boost_process_v2_${name} Boost::system Boost::filesystem boost_process_v2_test_impl) + target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl) add_test(NAME boost_process_v2_${name} COMMAND $ ) endfunction() @@ -19,12 +19,11 @@ boost_process_v2_standalone_test(environment) add_library(boost_process_v2_header_test header_1.cpp header_2.cpp) add_executable(boost_process_v2_test_target target.cpp) -target_link_libraries(boost_process_v2_test_target PUBLIC Boost::system) -target_include_directories(boost_process_v2_test_target PUBLIC ../../../..) +target_link_libraries(boost_process_v2_test_target PUBLIC Boost::process Boost::system) function(boost_process_v2_test_with_target name) add_executable(boost_process_v2_${name} ${name}.cpp) - target_link_libraries(boost_process_v2_${name} Boost::system Boost::filesystem boost_process_v2_test_impl) + target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl) add_dependencies(boost_process_v2_${name} boost_process_v2_test_target) add_test(NAME boost_process_v2_${name} COMMAND $ -- $) From 352b6cf89f5dbdc8675cdf7cff57697cc2d1fad5 Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 18 Aug 2022 21:17:42 +0800 Subject: [PATCH 108/397] Added github action yml --- .github/workflows/ci.yml | 375 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..0908e2e52 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,375 @@ +name: CI + +on: + pull_request: + push: + branches: + - master + - develop + - feature/** + +env: + UBSAN_OPTIONS: print_stacktrace=1 + +jobs: + posix: + strategy: + fail-fast: false + matrix: + include: + - toolset: gcc-4.8 + cxxstd: "11" + os: ubuntu-18.04 + install: g++-4.8 + - toolset: gcc-5 + cxxstd: "11,14,1z" + os: ubuntu-18.04 + install: g++-5 + - toolset: gcc-6 + cxxstd: "11,14,1z" + os: ubuntu-18.04 + install: g++-6 + - toolset: gcc-7 + cxxstd: "11,14,17" + os: ubuntu-18.04 + - toolset: gcc-8 + cxxstd: "11,14,17,2a" + os: ubuntu-18.04 + install: g++-8 + - toolset: gcc-9 + cxxstd: "11,14,17,2a" + os: ubuntu-20.04 + - toolset: gcc-10 + cxxstd: "11,14,17,2a" + os: ubuntu-20.04 + install: g++-10 + - toolset: gcc-11 + cxxstd: "11,14,17,2a" + os: ubuntu-20.04 + install: g++-11 + - 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-18.04 + install: clang-3.9 + - toolset: clang + compiler: clang++-4.0 + cxxstd: "11,14" + os: ubuntu-18.04 + install: clang-4.0 + - toolset: clang + compiler: clang++-5.0 + cxxstd: "11,14,1z" + os: ubuntu-18.04 + install: clang-5.0 + - toolset: clang + compiler: clang++-6.0 + cxxstd: "11,14,17" + os: ubuntu-18.04 + install: clang-6.0 + - toolset: clang + compiler: clang++-7 + cxxstd: "11,14,17" + os: ubuntu-18.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++-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-11 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Install packages + if: matrix.install + run: sudo apt install ${{matrix.install}} + + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY + LIBRARY=${GITHUB_REPOSITORY#*/} + echo LIBRARY: $LIBRARY + echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV + echo GITHUB_BASE_REF: $GITHUB_BASE_REF + echo GITHUB_REF: $GITHUB_REF + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + REF=${REF#refs/heads/} + echo REF: $REF + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + echo BOOST_BRANCH: $BOOST_BRANCH + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + ./bootstrap.sh + ./b2 -d0 headers + + - name: Create user-config.jam + if: matrix.compiler + run: | + echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam + + - name: Run tests + run: | + cd ../boost-root + ./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} variant=debug,release + + windows: + strategy: + fail-fast: false + matrix: + include: + - toolset: msvc-14.0 + cxxstd: "14,latest" + addrmd: 32,64 + os: windows-2019 + - toolset: msvc-14.2 + cxxstd: "14,17,20,latest" + addrmd: 32,64 + os: windows-2019 + - toolset: msvc-14.3 + cxxstd: "14,17,20,latest" + addrmd: 32,64 + os: windows-2022 + - toolset: clang-win + cxxstd: "14,17,latest" + addrmd: 32,64 + os: windows-2022 + - toolset: gcc + cxxstd: "11,14,17,2a" + addrmd: 64 + os: windows-2019 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Setup Boost + shell: cmd + run: | + echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% + for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi + echo LIBRARY: %LIBRARY% + echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% + echo GITHUB_BASE_REF: %GITHUB_BASE_REF% + echo GITHUB_REF: %GITHUB_REF% + if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% + set BOOST_BRANCH=develop + for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master + echo BOOST_BRANCH: %BOOST_BRANCH% + cd .. + git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% + cmd /c bootstrap + b2 -d0 headers + + - name: Run tests + shell: cmd + run: | + cd ../boost-root + b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release embed-manifest-via=linker + + posix-cmake-subdir: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-18.04 + - os: ubuntu-20.04 + - os: ubuntu-22.04 + - os: macos-11 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Install packages + if: matrix.install + run: sudo apt install ${{matrix.install}} + + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY + LIBRARY=${GITHUB_REPOSITORY#*/} + echo LIBRARY: $LIBRARY + echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV + echo GITHUB_BASE_REF: $GITHUB_BASE_REF + echo GITHUB_REF: $GITHUB_REF + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + REF=${REF#refs/heads/} + echo REF: $REF + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + echo BOOST_BRANCH: $BOOST_BRANCH + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + + - name: Use library with add_subdirectory + run: | + cd ../boost-root/libs/$LIBRARY/test/cmake_subdir_test + mkdir __build__ && cd __build__ + cmake .. + cmake --build . + ctest --output-on-failure --no-tests=error + + posix-cmake-install: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-18.04 + - os: ubuntu-20.04 + - os: ubuntu-22.04 + - os: macos-11 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Install packages + if: matrix.install + run: sudo apt install ${{matrix.install}} + + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY + LIBRARY=${GITHUB_REPOSITORY#*/} + echo LIBRARY: $LIBRARY + echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV + echo GITHUB_BASE_REF: $GITHUB_BASE_REF + echo GITHUB_REF: $GITHUB_REF + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + REF=${REF#refs/heads/} + echo REF: $REF + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + echo BOOST_BRANCH: $BOOST_BRANCH + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + + - name: Configure + run: | + cd ../boost-root + mkdir __build__ && cd __build__ + cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DCMAKE_INSTALL_PREFIX=~/.local .. + + - name: Install + run: | + cd ../boost-root/__build__ + cmake --build . --target install + + - name: Use the installed library + run: | + cd ../boost-root/libs/$LIBRARY/test/cmake_install_test && mkdir __build__ && cd __build__ + cmake -DCMAKE_INSTALL_PREFIX=~/.local .. + cmake --build . + ctest --output-on-failure --no-tests=error + + posix-cmake-test: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-18.04 + - os: ubuntu-20.04 + - os: ubuntu-22.04 + - os: macos-11 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Install packages + if: matrix.install + run: sudo apt install ${{matrix.install}} + + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY + LIBRARY=${GITHUB_REPOSITORY#*/} + echo LIBRARY: $LIBRARY + echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV + echo GITHUB_BASE_REF: $GITHUB_BASE_REF + echo GITHUB_REF: $GITHUB_REF + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + REF=${REF#refs/heads/} + echo REF: $REF + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + echo BOOST_BRANCH: $BOOST_BRANCH + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + + - name: Configure + run: | + cd ../boost-root + mkdir __build__ && cd __build__ + cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DBUILD_TESTING=ON .. + + - name: Build tests + run: | + cd ../boost-root/__build__ + cmake --build . --target tests + + - name: Run tests + run: | + cd ../boost-root/__build__ + ctest --output-on-failure --no-tests=error From 9a1c6991c982a2d8a378b33a331110acadd44a7e Mon Sep 17 00:00:00 2001 From: Klemens Date: Thu, 18 Aug 2022 22:02:33 +0800 Subject: [PATCH 109/397] Fixed cmake build --- test/CMakeLists.txt | 16 ++++++++++------ test/v2/CMakeLists.txt | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7ffb08113..2a8936cdd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,7 +2,7 @@ enable_testing() add_executable(boost_process_sparring_partner sparring_partner.cpp ) -target_link_libraries(boost_process_sparring_partner Boost::process Boost::program_options Boost::filesystem Boost::iostreams) +target_link_libraries(boost_process_sparring_partner Boost::process Boost::lambda Boost::program_options Boost::filesystem Boost::iostreams) add_executable(boost_process_exit_argc exit_argc.cpp) @@ -11,7 +11,7 @@ target_link_libraries(boost_process_sub_launch Boost::process Boost::program_opt function(process_standalone_test name ) add_executable(boost_process_${name} ${name}.cpp) - target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem) + target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::unit_test_framework) add_test(NAME boost_process_${name} COMMAND $ ) endfunction() @@ -21,7 +21,7 @@ process_standalone_test(pipe) function(process_sub_launch_test name ) add_executable(boost_process_${name} ${name}.cpp) - target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread) + target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread Boost::unit_test_framework) add_test(NAME boost_process_${name} COMMAND $ $ ) endfunction() @@ -30,7 +30,7 @@ process_sub_launch_test(group_wait) function(process_sparring_partner_launch name ) add_executable(boost_process_${name} ${name}.cpp) - target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread) + target_link_libraries(boost_process_${name} Boost::process Boost::system Boost::filesystem Boost::thread Boost::unit_test_framework Boost::program_options) add_test(NAME boost_process_${name} COMMAND $ $ ) endfunction() @@ -70,7 +70,11 @@ process_sparring_partner_launch(wait_for) process_sparring_partner_launch(on_exit) process_sparring_partner_launch(on_exit2) process_sparring_partner_launch(on_exit3) -process_sparring_partner_launch(posix_specific) -process_sparring_partner_launch(windows_specific) + +if(WIN32) + process_sparring_partner_launch(windows_specific) +else() + process_sparring_partner_launch(posix_specific) +endif() add_subdirectory(v2) \ No newline at end of file diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index 247afdabc..e75d705a8 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -17,7 +17,7 @@ boost_process_v2_standalone_test(pid) boost_process_v2_standalone_test(environment) add_library(boost_process_v2_header_test header_1.cpp header_2.cpp) - +target_link_libraries(boost_process_v2_header_test PUBLIC Boost::process) add_executable(boost_process_v2_test_target target.cpp) target_link_libraries(boost_process_v2_test_target PUBLIC Boost::process Boost::system) From 4ef1792b0a45af062101522ddb5087f76f21e1c6 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 19 Aug 2022 00:03:26 +0800 Subject: [PATCH 110/397] Typo fix. --- include/boost/process/async_pipe.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/async_pipe.hpp b/include/boost/process/async_pipe.hpp index a562432c0..0982b9041 100644 --- a/include/boost/process/async_pipe.hpp +++ b/include/boost/process/async_pipe.hpp @@ -26,7 +26,7 @@ namespace boost { namespace process { #if defined(BOOST_PROCESS_DOXYGEN) -/** Class implementing and asnychronous I/O-Object for use with boost.asio. +/** Class implementing an asnychronous 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. * From b0da4ad10c522c254229598b14e8f6428aafec83 Mon Sep 17 00:00:00 2001 From: grtowel1510f <101930220+grtowel1510f@users.noreply.github.com> Date: Sat, 21 May 2022 14:59:37 +0000 Subject: [PATCH 111/397] fix issue #251 - fix simple shell command in posix see issue #251 for description. --- include/boost/process/detail/posix/basic_cmd.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/detail/posix/basic_cmd.hpp b/include/boost/process/detail/posix/basic_cmd.hpp index 326e30dc0..d13d4c1bc 100644 --- a/include/boost/process/detail/posix/basic_cmd.hpp +++ b/include/boost/process/detail/posix/basic_cmd.hpp @@ -139,7 +139,7 @@ struct exe_cmd_init : boost::process::detail::api::handler_base_ext } static exe_cmd_init cmd_shell(std::string&& cmd) { - std::vector args = {"-c", "\"" + cmd + "\""}; + std::vector args = {"-c", cmd}; std::string sh = shell().string(); return exe_cmd_init( From ba7fe11193299dc9797a3e28129d77986c3a642c Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 19 Aug 2022 00:49:57 +0800 Subject: [PATCH 112/397] Added reaping child for execve error, closes #265. --- include/boost/process/detail/posix/executor.hpp | 3 +++ test/error.cpp | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index 8b9328c3e..48e69cc92 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -444,6 +444,8 @@ child executor::invoke(boost::mpl::false_, boost::mpl::false_) } if (_ec) { + //if an error occured we need to reap the child process + ::waitpid(this->pid, nullptr, WNOHANG); boost::fusion::for_each(seq, call_on_error(*this, _ec)); return child(); } @@ -537,6 +539,7 @@ child executor::invoke(boost::mpl::false_, boost::mpl::true_) if (_ec) { + ::waitpid(this->pid, nullptr, WNOHANG); boost::fusion::for_each(seq, call_on_error(*this, _ec)); return child(); } diff --git a/test/error.cpp b/test/error.cpp index a1c3007f3..151aea9b1 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -113,3 +113,17 @@ BOOST_AUTO_TEST_CASE(ignore_error) BOOST_CHECK_NO_THROW(bp::child c("doesnt-exit", bp::ignore_error)); } } + + +BOOST_AUTO_TEST_CASE(not_found) +{ + try + { + bp::child c("doesnt-exit"); + BOOST_CHECK_MESSAGE(false, "Should throw"); + } + catch( bp::process_error & se) + { + BOOST_CHECK(se.code()); + } +} From b68900ca1ca0471652feb9a22eb4d042d4b7e366 Mon Sep 17 00:00:00 2001 From: Klemens Date: Tue, 30 Aug 2022 15:51:22 +0800 Subject: [PATCH 113/397] Fixed unsafe post-fork allocs for fd_whitelist. --- include/boost/process/v2/posix/bind_fd.hpp | 7 +++++-- include/boost/process/v2/posix/default_launcher.hpp | 2 +- include/boost/process/v2/posix/pdfork_launcher.hpp | 2 +- include/boost/process/v2/stdio.hpp | 5 ----- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/boost/process/v2/posix/bind_fd.hpp b/include/boost/process/v2/posix/bind_fd.hpp index bb0ab08b9..6b46e236c 100644 --- a/include/boost/process/v2/posix/bind_fd.hpp +++ b/include/boost/process/v2/posix/bind_fd.hpp @@ -91,13 +91,16 @@ struct bind_fd { } + error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *) + { + launcher.fd_whitelist.push_back(target); + } + /// Implementation of the initialization function. error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *) { if (::dup2(fd, target) == -1) return error_code(errno, system_category()); - - launcher.fd_whitelist.push_back(target); return error_code (); } }; diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index 0486f0696..908ab4f5c 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -378,6 +378,7 @@ struct default_launcher detail::on_error(*this, executable, argv, ec, inits...); return basic_process(exec); } + fd_whitelist.push_back(pg.p[1]); auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query( exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context); @@ -399,7 +400,6 @@ struct default_launcher ec = detail::on_exec_setup(*this, executable, argv, inits...); if (!ec) { - fd_whitelist.push_back(pg.p[1]); close_all_fds(ec); } if (!ec) diff --git a/include/boost/process/v2/posix/pdfork_launcher.hpp b/include/boost/process/v2/posix/pdfork_launcher.hpp index 98985995e..80f79c93c 100644 --- a/include/boost/process/v2/posix/pdfork_launcher.hpp +++ b/include/boost/process/v2/posix/pdfork_launcher.hpp @@ -99,6 +99,7 @@ struct pdfork_launcher : default_launcher detail::on_error(*this, executable, argv, ec, inits...); return basic_process(exec); } + fd_whitelist.push_back(pg.p[1]); auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query( exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context); @@ -121,7 +122,6 @@ struct pdfork_launcher : default_launcher ec = detail::on_exec_setup(*this, executable, argv, inits...); if (!ec) { - fd_whitelist.push_back(pg.p[1]); close_all_fds(ec); } if (!ec) diff --git a/include/boost/process/v2/stdio.hpp b/include/boost/process/v2/stdio.hpp index 5e0320eef..fd43eacfd 100644 --- a/include/boost/process/v2/stdio.hpp +++ b/include/boost/process/v2/stdio.hpp @@ -289,11 +289,6 @@ struct process_stdio if (::dup2(err.fd, err.target) == -1) return error_code(errno, system_category()); - - launcher.fd_whitelist.push_back(STDIN_FILENO); - launcher.fd_whitelist.push_back(STDOUT_FILENO); - launcher.fd_whitelist.push_back(STDERR_FILENO); - return error_code {}; }; #endif From 2c35167d9be73d61c3d40c19f62f57baed87c836 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 30 Aug 2022 17:12:14 +0800 Subject: [PATCH 114/397] Closes #266 --- include/boost/process/detail/posix/executor.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index 48e69cc92..316d21208 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -155,8 +155,8 @@ class executor void write_error(const std::error_code & ec, const char * msg) { //I am the child - const auto len = std::strlen(msg); - int data[2] = {ec.value(), static_cast(len + 1)}; + const auto len = static_cast(std::strlen(msg)); + int data[2] = {ec.value(), len + 1}; boost::ignore_unused(::write(_pipe_sink, &data[0], sizeof(int) * 2)); boost::ignore_unused(::write(_pipe_sink, msg, len)); From ae6a9e663976b94e0f4567cfe741977c1ea748e9 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Tue, 30 Aug 2022 17:08:30 +0800 Subject: [PATCH 115/397] Closes #267 --- include/boost/process/environment.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/process/environment.hpp b/include/boost/process/environment.hpp index 3aa4c9160..dc9e99df5 100644 --- a/include/boost/process/environment.hpp +++ b/include/boost/process/environment.hpp @@ -263,7 +263,7 @@ class basic_environment_impl : public Implementation auto st1 = key + ::boost::process::detail::equal_sign(); while (*p != nullptr) { - const int len = std::char_traits::length(*p); + const auto len = std::char_traits::length(*p); if ((std::distance(st1.begin(), st1.end()) < len) && std::equal(st1.begin(), st1.end(), *p)) break; From 4761b375d0d00842a406d400e010fe01f46a24fb Mon Sep 17 00:00:00 2001 From: Klemens Date: Wed, 31 Aug 2022 23:54:22 +0800 Subject: [PATCH 116/397] Added shell class. --- include/boost/process/v2/impl/shell.ipp | 100 ++++++++++++++++++++ include/boost/process/v2/shell.hpp | 117 ++++++++++++++++++++++++ include/boost/process/v2/src.hpp | 1 + test/v2/CMakeLists.txt | 1 + test/v2/Jamfile.jam | 1 + test/v2/shell.cpp | 45 +++++++++ 6 files changed, 265 insertions(+) create mode 100644 include/boost/process/v2/impl/shell.ipp create mode 100644 include/boost/process/v2/shell.hpp create mode 100755 test/v2/shell.cpp diff --git a/include/boost/process/v2/impl/shell.ipp b/include/boost/process/v2/impl/shell.ipp new file mode 100644 index 000000000..88e9d0f14 --- /dev/null +++ b/include/boost/process/v2/impl/shell.ipp @@ -0,0 +1,100 @@ +// 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_IMPL_SHELL_IPP +#define BOOST_PROCESS_V2_IMPL_SHELL_IPP + +#include +#include +#include +#include +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) +#include +#else +#include +#endif + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + +#if defined(BOOST_PROCESS_V2_WINDOWS) +BOOST_PROCESS_V2_DECL const error_category& get_shell_category() +{ + return system_category(); +} +#else + +struct shell_category_t final : public error_category +{ + shell_category_t() : error_category(0xDAF1u) {} + + const char* name() const noexcept + { + return "process.v2.utf8"; + } + std::string message(int value) const + { + switch (value) + { + case WRDE_BADCHAR: + return "Illegal occurrence of newline or one of |, &, ;, <, >, (, ), {, }."; + case WRDE_BADVAL: + return "An undefined shell variable was referenced, and the WRDE_UNDEF flag told us to consider this an error."; + case WRDE_CMDSUB: + return "Command substitution occurred, and the WRDE_NOCMD flag told us to consider this an error."; + case WRDE_NOSPACE: + return "Out of memory."; + case WRDE_SYNTAX: + return "Shell syntax error, such as unbalanced parentheses or unmatched quotes."; + default: + return "process.v2.wordexp error"; + } + } +}; + +BOOST_PROCESS_V2_DECL const error_category& get_shell_category() +{ + static shell_category_t instance; + return instance; +} + +#endif + +shell::argv_t shell::argv_t::parse_( + basic_cstring_ref input, + error_code & ec) +{ + shell::argv_t res; + wordexp_t we{}; + auto cd = wordexp(input.c_str(), &we, WRDE_NOCMD); + + if (cd != 0) + ec.assign(cd, get_shell_category()); + else + { + res.argc_ = static_cast(we.we_wordc); + res.argv_ = we.we_wordv; + res.reserved_ = static_cast(we.we_offs); + } + + return res; +} + +shell::argv_t::~argv_t() +{ + if (argv_ != nullptr) + { + wordexp_t we{ + .we_wordc = static_cast(argc_), + .we_wordv = argv_, + .we_offs = static_cast(reserved_) + }; + wordfree(&we); + } +} + +BOOST_PROCESS_V2_END_NAMESPACE + +#endif //BOOST_PROCESS_V2_IMPL_SHELL_IPP diff --git a/include/boost/process/v2/shell.hpp b/include/boost/process/v2/shell.hpp new file mode 100644 index 000000000..de4412ebb --- /dev/null +++ b/include/boost/process/v2/shell.hpp @@ -0,0 +1,117 @@ +// 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_SHELL_HPP +#define BOOST_PROCESS_V2_SHELL_HPP + +#include +#include +#include +#include +#include + +BOOST_PROCESS_V2_BEGIN_NAMESPACE + + +extern BOOST_PROCESS_V2_DECL const error_category& get_shell_category(); +static const error_category& shell_category = get_shell_category(); + +struct shell +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) + using char_type = wchar_t; +#else + using char_type = char; +#endif + + template + shell(basic_cstring_ref input) + : buffer_(conv_string(input.data(), input.size())) + { + } + + shell(basic_cstring_ref input) : input_(input) {} + + struct argv_t + { + using char_type = shell::char_type; + + int argc() const { return argc_; } + char_type** argv() const { return argv_; } + + char_type** begin() const {return argv();} + char_type** end() const {return argv() + argc();} + + bool empty() const {return argc() == 0;} + + argv_t() = default; + argv_t(basic_cstring_ref input) + { + error_code ec; + *this = parse_(input, ec); + if (ec) + detail::throw_error(ec, "parse-argv"); + } + argv_t(const argv_t &) = delete; + argv_t& operator=(const argv_t &) = delete; + + argv_t(argv_t && lhs) noexcept + : argc_(boost::exchange(lhs.argc_, 0)), + argv_(boost::exchange(lhs.argv_, nullptr)), + reserved_(boost::exchange(lhs.reserved_, 0)) + { + } + argv_t& operator=(argv_t && lhs) noexcept + { + argv_t tmp(std::move(*this)); + argc_ = boost::exchange(lhs.argc_, 0); + argv_ = boost::exchange(lhs.argv_, nullptr); + reserved_ = boost::exchange(lhs.reserved_, 0); + return *this; + } + BOOST_PROCESS_V2_DECL ~argv_t(); + private: + BOOST_PROCESS_V2_DECL static + argv_t parse_(basic_cstring_ref input, error_code & ec); + friend struct shell; + int argc_ = 0; + char_type ** argv_ = nullptr; + int reserved_ = 0; + }; + + argv_t parse() const + { + error_code ec; + auto tmp = parse(ec); + if (ec) + detail::throw_error(ec, "parse cmd_line"); + return tmp; + } + + argv_t parse(error_code & ec) const noexcept + { + return argv_t::parse_(cmd_line(), ec); + } + + basic_cstring_ref cmd_line() const noexcept + { + return buffer_.empty() ? input_ : buffer_.c_str(); + } + + private: + // storage in case we need a conversion + std::basic_string buffer_; + basic_cstring_ref input_; +}; + +BOOST_PROCESS_V2_END_NAMESPACE + +#if defined(BOOST_PROCESS_V2_HEADER_ONLY) + +#include + +#endif + +#endif //BOOST_PROCESS_V2_ERROR_HPP diff --git a/include/boost/process/v2/src.hpp b/include/boost/process/v2/src.hpp index 60c505ce6..7bf38448b 100644 --- a/include/boost/process/v2/src.hpp +++ b/include/boost/process/v2/src.hpp @@ -22,5 +22,6 @@ #include #include #include +#include #endif //BOOST_PROCESS_V2_SRC_HPP diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index e75d705a8..4ee98e40e 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -15,6 +15,7 @@ boost_process_v2_standalone_test(utf8) boost_process_v2_standalone_test(cstring_ref) boost_process_v2_standalone_test(pid) boost_process_v2_standalone_test(environment) +boost_process_v2_standalone_test(shell) add_library(boost_process_v2_header_test header_1.cpp header_2.cpp) target_link_libraries(boost_process_v2_header_test PUBLIC Boost::process) diff --git a/test/v2/Jamfile.jam b/test/v2/Jamfile.jam index fb4575dea..ef4a06792 100644 --- a/test/v2/Jamfile.jam +++ b/test/v2/Jamfile.jam @@ -53,6 +53,7 @@ test-suite standalone : [ run cstring_ref.cpp test_impl ] [ run environment.cpp test_impl ] [ run pid.cpp test_impl ] + [ run shell.cpp test_impl ] ; test-suite with_target : diff --git a/test/v2/shell.cpp b/test/v2/shell.cpp new file mode 100755 index 000000000..52a6eaa48 --- /dev/null +++ b/test/v2/shell.cpp @@ -0,0 +1,45 @@ +// 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) + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include + +#include + +#if defined(BOOST_PROCESS_V2_WINDOWS) + #define STR(Value) L##Value + #define STR_VIEW(Value) boost::process::v2::wcstring_ref(STR(Value)) +#else + #define STR(Value) Value + #define STR_VIEW(Value) boost::process::v2::cstring_ref(STR(Value)) +#endif + + +BOOST_AUTO_TEST_CASE(test_shell_parser) +{ + using boost::process::v2::shell; + + auto sh = shell(STR("foo \"")); + + boost::system::error_code ec; + auto argv = sh.parse(ec); + BOOST_CHECK(argv.empty()); + BOOST_CHECK(ec); + + sh = shell(STR("foo bar \"foo bar\"")); + + ec.clear(); + argv = sh.parse(ec); + BOOST_CHECK(argv.argc() == 3u); + BOOST_CHECK(!ec); + BOOST_CHECK(argv.argv()[0] == STR_VIEW("foo")); + BOOST_CHECK(argv.argv()[1] == STR_VIEW("bar")); + BOOST_CHECK(argv.argv()[2] == STR_VIEW("foo bar")); +} \ No newline at end of file From ecf3dde88c0608db8a892c9f1999966fe7a19809 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 2 Sep 2022 16:46:45 +0800 Subject: [PATCH 117/397] Windows bugfixes. --- .../v2/detail/impl/process_handle_windows.ipp | 8 +- .../v2/detail/process_handle_windows.hpp | 8 +- include/boost/process/v2/environment.hpp | 86 ++++++++----------- include/boost/process/v2/process.hpp | 8 +- include/boost/process/v2/stdio.hpp | 23 +++-- test/v2/environment.cpp | 10 +-- test/v2/process.cpp | 22 +++-- test/v2/windows.cpp | 2 +- 8 files changed, 82 insertions(+), 85 deletions(-) diff --git a/include/boost/process/v2/detail/impl/process_handle_windows.ipp b/include/boost/process/v2/detail/impl/process_handle_windows.ipp index bbc324bb8..964224807 100644 --- a/include/boost/process/v2/detail/impl/process_handle_windows.ipp +++ b/include/boost/process/v2/detail/impl/process_handle_windows.ipp @@ -77,14 +77,14 @@ static BOOL CALLBACK enum_window(HWND hwnd, LPARAM param) auto data = reinterpret_cast(param); DWORD pid{0u}; GetWindowThreadProcessId(hwnd, &pid); - if (pid != data->pid) return TRUE; - + LRESULT res = ::SendMessageW(hwnd, WM_CLOSE, 0, 0); - if (!res) + + if (res) data->ec = detail::get_last_error(); - return res != 0; + return res == 0; } void request_exit_(pid_type pid_, error_code & ec) diff --git a/include/boost/process/v2/detail/process_handle_windows.hpp b/include/boost/process/v2/detail/process_handle_windows.hpp index bd8441ab7..275d05511 100644 --- a/include/boost/process/v2/detail/process_handle_windows.hpp +++ b/include/boost/process/v2/detail/process_handle_windows.hpp @@ -87,17 +87,16 @@ struct basic_process_handle_win { } - basic_process_handle_win(basic_process_handle_win && handle) + basic_process_handle_win(basic_process_handle_win && handle) + : pid_(handle.id()), handle_(std::move(handle.handle_)) { - pid_ = handle.id(); - handle_ = std::move(handle.handle_); handle.pid_ = static_cast(-1); } basic_process_handle_win& operator=(basic_process_handle_win && handle) { pid_ = handle.pid_; - handle_ = std::mopve(handle_)) + handle_ = std::move(handle.handle_); handle.pid_ = static_cast(-1); return *this; } @@ -166,7 +165,6 @@ struct basic_process_handle_win { if (!detail::check_pid_(pid_, ec)) return; - detail::request_exit_(pid_, ec); } diff --git a/include/boost/process/v2/environment.hpp b/include/boost/process/v2/environment.hpp index 2016a6d9a..2814d1f56 100644 --- a/include/boost/process/v2/environment.hpp +++ b/include/boost/process/v2/environment.hpp @@ -979,7 +979,8 @@ struct key_value_pair const std::pair & kv/*, typename std::enable_if::value && std::is_constructible::value - >::type = 0*/) : value_(((struct key)(kv.first)).string() + equality_sign + ((struct value)(kv.second)).string()) + >::type = 0*/) : value_(((struct key)(kv.first)).basic_string() + equality_sign + + ((struct value)(kv.second)).basic_string()) {} key_value_pair(const typename conditional::value, wchar_t, char>::type * raw) @@ -1045,6 +1046,7 @@ struct key_value_pair operator string_type() const {return native();} operator string_view_type() const {return native_view();} + operator typename string_view_type::string_view_type() const {return native_view();} operator key_value_pair_view() const {return native_view();} int compare( const key_value_pair& p ) const noexcept @@ -1432,8 +1434,9 @@ auto find_key(Environment & env, key_view ky) template inline filesystem::path home(Environment && env = current()) { -#if defined(ASIO_WINDOWS) - return detail::find_key(env, L"HOMEDRIVE") + detail::find_key(env, L"HOMEPATH").native_string(); +#if defined(BOOST_PROCESS_V2_WINDOWS) + return detail::find_key(env, L"HOMEDRIVE").native_string() + + detail::find_key(env, L"HOMEPATH").native_string(); #else return detail::find_key(env, "HOME").native_string(); #endif @@ -1468,7 +1471,7 @@ inline BOOST_PROCESS_V2_NAMESPACE::filesystem::path find_executable( // first check if it has the extension already BOOST_PROCESS_V2_NAMESPACE::filesystem::path full_nm(name); BOOST_PROCESS_V2_NAMESPACE::filesystem::path pp(pp_view.begin(), pp_view.end()); - auto p = pp / nm; + auto p = pp / full_nm; error_code ec; if (detail::is_executable(p, ec) && !ec) @@ -1695,67 +1698,52 @@ struct process_environment template - void build_env(Args && args, string_view rs) + static + std::vector build_env(Args && args, + typename std::enable_if< + std::is_convertible< + decltype(*std::begin(std::declval())), + wcstring_ref>::value>::type * = nullptr) { - std::size_t length = 0u; - for (string_view v : args) - length += detail::size_as_wide(v.data(), v.size(), ec) + 1u; - - if (ec) - return; - length ++ ; - - unicode_env.resize(length); + std::vector res; + std::size_t sz = 1; + for (wcstring_ref cs : std::forward(args)) + sz =+ cs.size() + 1; + res.reserve(sz); + + for (wcstring_ref cs : std::forward(args)) + res.insert(res.end(), cs.begin(), std::next(cs.end())); + - auto itr = &unicode_env.front(); - for (string_view v : args) - { - itr += detail::convert_to_wide( - v.data(), v.size(), - itr, &unicode_env.back() - itr, - ec); - if (ec) - break; - *(itr++) = '\0'; - } - unicode_env.back() = '\0'; + res.push_back(L'\0'); + return res; } + template - void build_env(Args && args, wstring_view rs) + std::vector build_env(Args && args, + typename std::enable_if< + !std::is_convertible< + decltype(*std::begin(std::declval())), + wcstring_ref>::value>::type * = nullptr) { - std::size_t length = 0u; - for (const auto & v : std::forward(args)) - length += v.size() + 1u; - - length ++ ; - - unicode_env.resize(length); - - auto itr = unicode_env.begin(); - for (wstring_view v : args ) - { - itr = std::copy(v.begin(), v.end(), itr); - *(itr++) = L'\0'; - } - unicode_env.back() = L'\0'; + for (auto && arg: std::forward(args)) + env_buffer.emplace_back(arg); + return build_env(env_buffer); } - - process_environment(std::initializer_list sv) { build_env(sv, ""); } - process_environment(std::initializer_list sv) { build_env(sv, L""); } + process_environment(std::initializer_list sv) : unicode_env{build_env(sv, "")} {} + process_environment(std::initializer_list sv) : unicode_env{build_env(sv, L"")} {} template - process_environment(Args && args) + process_environment(Args && args) : unicode_env{build_env(std::forward(args))} { - if (std::begin(args) != std::end(args)) - build_env(std::forward(args), *std::begin(args)); } error_code error() {return ec;} error_code ec; + std::vector env_buffer; std::vector unicode_env; - error_code on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &); diff --git a/include/boost/process/v2/process.hpp b/include/boost/process/v2/process.hpp index 7acd4d327..798d1394f 100644 --- a/include/boost/process/v2/process.hpp +++ b/include/boost/process/v2/process.hpp @@ -19,9 +19,11 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) #include +#include #include #else #include +#include #include #endif @@ -164,7 +166,7 @@ struct basic_process typename std::enable_if< std::is_convertible::value, void *>::type = nullptr) - : process_handle_(context, pid, native_handle) {} + : process_handle_(context.get_executor(), pid, native_handle) {} /// Create an invalid handle template @@ -172,7 +174,7 @@ struct basic_process typename std::enable_if< is_convertible::value, void *>::type = nullptr) - : process_handle_(context) {} + : process_handle_(context.get_executor()) {} @@ -339,7 +341,7 @@ struct basic_process }; BOOST_PROCESS_V2_ASIO_NAMESPACE::post(handle.get_executor(), - completer{res, std::move(self)}); + completer{static_cast(res), std::move(self)}); } else handle.async_wait(std::move(self)); diff --git a/include/boost/process/v2/stdio.hpp b/include/boost/process/v2/stdio.hpp index fd43eacfd..000ffe62a 100644 --- a/include/boost/process/v2/stdio.hpp +++ b/include/boost/process/v2/stdio.hpp @@ -12,7 +12,7 @@ #include #include - +#include #if defined(BOOST_PROCESS_V2_STANDALONE) #include #else @@ -52,7 +52,7 @@ struct handle_closer DWORD flags{0xFFFFFFFFu}; }; -template +template struct process_io_binding { HANDLE prepare() @@ -62,7 +62,7 @@ struct process_io_binding return hh; } - std::unique_ptr h{::GetStdHandle(Io), false}; + std::unique_ptr h{::GetStdHandle(Target), false}; static DWORD get_flags(HANDLE h) { @@ -82,10 +82,11 @@ struct process_io_binding process_io_binding(FILE * f) : process_io_binding(_get_osfhandle(_fileno(f))) {} process_io_binding(HANDLE h) : h{h, get_flags(h)} {} process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("NUL")) {} - process_io_binding(const filesystem::path & pth) + template::value>::type> + process_io_binding(const T & pth) : h(::CreateFileW( pth.c_str(), - Io == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE, + Target == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, @@ -101,11 +102,13 @@ struct process_io_binding typename std::enable_if::type = 0) { BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2]; + error_code ec; BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec); if (ec) - return ; + detail::throw_error(ec, "create_pipe"); + h = std::unique_ptr{p[1], true}; - readable_pipe.assign(p[0], ec); + readable_pipe.assign(p[0]); } @@ -114,11 +117,13 @@ struct process_io_binding typename std::enable_if::type = 0) { BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2]; + error_code ec; BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec); if (ec) - return ; + detail::throw_error(ec, "create_pipe"); + h = std::unique_ptr{p[0], true}; - writable_pipe.assign(p[1], ec); + writable_pipe.assign(p[1]); } }; diff --git a/test/v2/environment.cpp b/test/v2/environment.cpp index 168a0b97b..58765f054 100644 --- a/test/v2/environment.cpp +++ b/test/v2/environment.cpp @@ -95,9 +95,9 @@ BOOST_AUTO_TEST_CASE(environment) #else std::unordered_map custom_env = { - L"HOME", L"/home/byzantium", - L"HOMEDRIVE", L"X:", - L"HOMEPATH", L"\\users\\theodora" + {L"HOME", L"/home/byzantium"}, + {L"HOMEDRIVE", L"X:"}, + {L"HOMEPATH", L"\\users\\theodora"} }; std::vector custom_env2 = @@ -106,8 +106,8 @@ BOOST_AUTO_TEST_CASE(environment) {L"HOMEDRIVE=X:"}, {L"HOMEPATH=\\users\\theodora"} }; - BOOST_CHECK_EQUAL(bpe::home(custom_env), L"X:\\Users\\theodora"); - BOOST_CHECK_EQUAL(bpe::home(custom_env2), L"X:\\Users\\theodora"); + BOOST_CHECK_EQUAL(bpe::home(custom_env), "X:\\users\\theodora"); + BOOST_CHECK_EQUAL(bpe::home(custom_env2), "X:\\users\\theodora"); #endif diff --git a/test/v2/process.cpp b/test/v2/process.cpp index d160b5dfa..f7c4aa379 100644 --- a/test/v2/process.cpp +++ b/test/v2/process.cpp @@ -164,6 +164,7 @@ BOOST_AUTO_TEST_CASE(request_exit) , asio::windows::show_window_minimized_not_active #endif ); + BOOST_CHECK(proc.running()); std::this_thread::sleep_for(std::chrono::milliseconds(250)); proc.request_exit(); proc.wait(); @@ -188,6 +189,8 @@ void trim_end(std::string & str) { auto itr = std::find_if(str.rbegin(), str.rend(), &std::char_traits::not_eof); str.erase(itr.base(), str.end()); + if (!str.empty() && str.back() == '\r') + str.pop_back(); } BOOST_AUTO_TEST_CASE(print_args_out) @@ -356,16 +359,16 @@ BOOST_AUTO_TEST_CASE(popen) // default CWD bpv::popen proc(ctx, pth, {"echo"}); - asio::write(proc, asio::buffer("FOOBAR")); - + auto written = asio::write(proc, asio::buffer("FOOBAR")); proc.get_stdin().close(); std::string res; boost::system::error_code ec; std::size_t n = asio::read(proc, asio::dynamic_buffer(res), ec); - res.resize(n - 1); - BOOST_CHECK_EQUAL(ec, asio::error::eof); + BOOST_CHECK(ec == asio::error::eof || ec == asio::error::broken_pipe); + BOOST_REQUIRE_GE(n, 1); // remove EOF + res.pop_back(); BOOST_CHECK_EQUAL(res, "FOOBAR"); proc.wait(); @@ -429,7 +432,7 @@ std::string read_env(const char * name, Inits && ... inits) BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message()); out.resize(sz); trim_end(out); - printf("Read env (%ld) %s: '%s'\n", sz, name, out.c_str()); + printf("Read env (%ld) %s: '%s'\n", static_cast(sz), name, out.c_str()); proc.wait(); BOOST_CHECK_EQUAL(proc.exit_code(), 0); @@ -449,12 +452,12 @@ BOOST_AUTO_TEST_CASE(environment) BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{sub_env})); sub_env.push_back("XYZ=ZYX"); - auto itr = std::find_if(sub_env.begin(), sub_env.end(), [](const bpv::environment::key_value_pair & kv) {return kv.key() == "PATH";}); + auto itr = std::find_if(sub_env.begin(), sub_env.end(), [](const bpv::environment::key_value_pair & kv) {return kv.key() == bpv::environment::key("PATH");}); path += static_cast(bpv::environment::delimiter); path += "/bar/foo"; bpv::environment::value pval = itr->value(); pval.push_back("/bar/foo"); - *itr = bpv::environment::key_value_pair("PATH", pval); + *itr = bpv::environment::key_value_pair(bpv::environment::key("PATH"), pval); BOOST_CHECK_EQUAL(path, read_env("PATH", bpv::process_environment{sub_env})); #if defined(BOOST_PROCESS_V2_WINDOWS) @@ -462,12 +465,13 @@ BOOST_AUTO_TEST_CASE(environment) BOOST_CHECK_EQUAL("FOO-BAR", read_env("FOOBAR", bpv::process_environment{L"FOOBAR=FOO-BAR", wpath.c_str()})); wpath += bpv::environment::delimiter; wpath += L"C:\\bar\\foo"; - BOOST_CHECK_EQUAL(wpath.substr(5), read_env("pATH", bpv::process_environment{wpath.c_str(), std::wstring(L"XYZ=ZYX")})); + BOOST_CHECK_EQUAL( + bpv::detail::conv_string(wpath.c_str() + 5, wpath.size() - 5) + , read_env("pATH", bpv::process_environment{wpath.c_str(), std::wstring(L"XYZ=ZYX")})); #endif BOOST_CHECK_EQUAL(read_env("PATH", bpv::process_environment(bpv::environment::current())), ::getenv("PATH")); } - BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/v2/windows.cpp b/test/v2/windows.cpp index 9b6d3c61d..c61d649b1 100644 --- a/test/v2/windows.cpp +++ b/test/v2/windows.cpp @@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(creation_flags) BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, 0); proc = bpv::process{ctx, master_test_suite().argv[1], {"creation-flags"}, bpv::windows::process_creation_flags()}; - BOOST_CHECK(proc); + BOOST_CHECK(proc.running()); BOOST_CHECK_EQUAL(proc.wait() & ~EXTENDED_STARTUPINFO_PRESENT, STARTF_TITLEISAPPID); } From b8108c508f4bf712efabe074dd3ababf9a63deba Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 2 Sep 2022 17:05:49 +0800 Subject: [PATCH 118/397] Implemented shell on windows. --- include/boost/process/v2/impl/shell.ipp | 26 +++++++++++++++++++++++++ include/boost/process/v2/shell.hpp | 9 +++++++-- test/v2/shell.cpp | 10 ++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/include/boost/process/v2/impl/shell.ipp b/include/boost/process/v2/impl/shell.ipp index 88e9d0f14..d931677d7 100644 --- a/include/boost/process/v2/impl/shell.ipp +++ b/include/boost/process/v2/impl/shell.ipp @@ -6,6 +6,7 @@ #define BOOST_PROCESS_V2_IMPL_SHELL_IPP #include +#include #include #include #include @@ -62,6 +63,10 @@ BOOST_PROCESS_V2_DECL const error_category& get_shell_category() #endif +#if defined (BOOST_PROCESS_V2_WINDOWS) + +#else + shell::argv_t shell::argv_t::parse_( basic_cstring_ref input, error_code & ec) @@ -95,6 +100,27 @@ shell::argv_t::~argv_t() } } +#endif + +shell::argv_t shell::argv_t::parse_( + basic_cstring_ref input, + error_code & ec) +{ + shell::argv_t res; + res.argv_ = ::CommandLineToArgvW(input.c_str(), &res.argc_); + if (res.argv_ == nullptr) + ec = detail::get_last_error(); + return res; +} + +shell::argv_t::~argv_t() +{ + if (argv_ != nullptr) + { + LocalFree(argv_); + } +} + BOOST_PROCESS_V2_END_NAMESPACE #endif //BOOST_PROCESS_V2_IMPL_SHELL_IPP diff --git a/include/boost/process/v2/shell.hpp b/include/boost/process/v2/shell.hpp index de4412ebb..3a07f1581 100644 --- a/include/boost/process/v2/shell.hpp +++ b/include/boost/process/v2/shell.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -27,12 +28,16 @@ struct shell #endif template - shell(basic_cstring_ref input) - : buffer_(conv_string(input.data(), input.size())) + shell(basic_string_view input) + : buffer_(detail::conv_string(input.data(), input.size())) { } shell(basic_cstring_ref input) : input_(input) {} + shell(basic_string_view< + typename std::conditional< + std::is_same::value, + wchar_t, char>::type> input) : buffer_(detail::conv_string(input.data(), input.size())) {} struct argv_t { diff --git a/test/v2/shell.cpp b/test/v2/shell.cpp index 52a6eaa48..f29d3ca45 100755 --- a/test/v2/shell.cpp +++ b/test/v2/shell.cpp @@ -26,12 +26,18 @@ BOOST_AUTO_TEST_CASE(test_shell_parser) { using boost::process::v2::shell; - auto sh = shell(STR("foo \"")); - + auto sh = shell("foo \""); boost::system::error_code ec; auto argv = sh.parse(ec); + +#if defined(BOOST_PROCESS_V2_POSIX) + for (auto s : sh.parse()) + { + std::wcout << s << std::endl; + } BOOST_CHECK(argv.empty()); BOOST_CHECK(ec); +#endif sh = shell(STR("foo bar \"foo bar\"")); From ebd4e723c39abe0008dc82c29a1fcd699423955a Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Fri, 2 Sep 2022 18:25:40 +0800 Subject: [PATCH 119/397] Exeuction support for shell. --- include/boost/process/v2/impl/shell.ipp | 58 ++++--- include/boost/process/v2/shell.hpp | 145 ++++++++++-------- .../process/v2/windows/default_launcher.hpp | 5 + 3 files changed, 119 insertions(+), 89 deletions(-) diff --git a/include/boost/process/v2/impl/shell.ipp b/include/boost/process/v2/impl/shell.ipp index d931677d7..48a29aed9 100644 --- a/include/boost/process/v2/impl/shell.ipp +++ b/include/boost/process/v2/impl/shell.ipp @@ -65,29 +65,45 @@ BOOST_PROCESS_V2_DECL const error_category& get_shell_category() #if defined (BOOST_PROCESS_V2_WINDOWS) +void shell::parse_() +{ + argv_ = ::CommandLineToArgvW(input_.c_str(), &argc_); + if (argv_ == nullptr) + detail::throw_last_error(); +} + +shell::~shell() +{ + if (argv_ != nullptr) + LocalFree(argv_); +} + +auto shell::args() const-> args_type +{ + return input_.c_str(); +} + #else -shell::argv_t shell::argv_t::parse_( - basic_cstring_ref input, - error_code & ec) +shell::parse_() { shell::argv_t res; wordexp_t we{}; - auto cd = wordexp(input.c_str(), &we, WRDE_NOCMD); + auto cd = wordexp(input_.c_str(), &we, WRDE_NOCMD); if (cd != 0) - ec.assign(cd, get_shell_category()); + detail::throw_error(error_code(cd, get_shell_category()), "shell::parse"); else { - res.argc_ = static_cast(we.we_wordc); - res.argv_ = we.we_wordv; - res.reserved_ = static_cast(we.we_offs); + argc_ = static_cast(we.we_wordc); + argv_ = we.we_wordv; + reserved_ = static_cast(we.we_offs); } return res; } -shell::argv_t::~argv_t() +shell::~shell() { if (argv_ != nullptr) { @@ -100,27 +116,19 @@ shell::argv_t::~argv_t() } } -#endif - -shell::argv_t shell::argv_t::parse_( - basic_cstring_ref input, - error_code & ec) -{ - shell::argv_t res; - res.argv_ = ::CommandLineToArgvW(input.c_str(), &res.argc_); - if (res.argv_ == nullptr) - ec = detail::get_last_error(); - return res; -} - -shell::argv_t::~argv_t() +auto shell::args() const -> args_type { - if (argv_ != nullptr) + if (argc() == 0) { - LocalFree(argv_); + static char * helper = nullptr; + return &helper; } + else + return argv() + 1; } +#endif + BOOST_PROCESS_V2_END_NAMESPACE #endif //BOOST_PROCESS_V2_IMPL_SHELL_IPP diff --git a/include/boost/process/v2/shell.hpp b/include/boost/process/v2/shell.hpp index 3a07f1581..b58d2b41b 100644 --- a/include/boost/process/v2/shell.hpp +++ b/include/boost/process/v2/shell.hpp @@ -6,109 +6,126 @@ #define BOOST_PROCESS_V2_SHELL_HPP #include +#include #include #include #include -#include +#include #include +#include BOOST_PROCESS_V2_BEGIN_NAMESPACE - +/// Error category used by the shell parser. extern BOOST_PROCESS_V2_DECL const error_category& get_shell_category(); static const error_category& shell_category = get_shell_category(); +/// Utility to parse commands +/** This utility class parses command lines into tokens + * and allows users to executed based on textual inputs. + * + * In v1, this was possible directly when starting a process, + * but has been removed based on the security risks associated with this. + * + * By making the shell parsing explicity, it is encouraged + * that a user runs a sanity check on the executable before launching it. + * + * @par Example + * @code {.cpp} + * asio::io_context ctx; + * + * auto cmd = shell("my-app --help"); + * auto exe = cmd.exe(); + * check_if_malicious(exe); + * + * process proc{ctx, exe, cmd.args()}; + * + * @endcode + * + * + */ struct shell -{ - +{ #if defined(BOOST_PROCESS_V2_WINDOWS) using char_type = wchar_t; + using args_type = const wchar_t *; #else using char_type = char; + using args_type = const char **; #endif + shell() = default; + template shell(basic_string_view input) : buffer_(detail::conv_string(input.data(), input.size())) { + parse_(); } - shell(basic_cstring_ref input) : input_(input) {} + shell(basic_cstring_ref input) : input_(input) {parse_();} shell(basic_string_view< typename std::conditional< std::is_same::value, - wchar_t, char>::type> input) : buffer_(detail::conv_string(input.data(), input.size())) {} - - struct argv_t + wchar_t, char>::type> input) : buffer_(detail::conv_string(input.data(), input.size())) { - using char_type = shell::char_type; + parse_(); + } - int argc() const { return argc_; } - char_type** argv() const { return argv_; } - - char_type** begin() const {return argv();} - char_type** end() const {return argv() + argc();} - - bool empty() const {return argc() == 0;} - - argv_t() = default; - argv_t(basic_cstring_ref input) - { - error_code ec; - *this = parse_(input, ec); - if (ec) - detail::throw_error(ec, "parse-argv"); - } - argv_t(const argv_t &) = delete; - argv_t& operator=(const argv_t &) = delete; - - argv_t(argv_t && lhs) noexcept - : argc_(boost::exchange(lhs.argc_, 0)), - argv_(boost::exchange(lhs.argv_, nullptr)), - reserved_(boost::exchange(lhs.reserved_, 0)) - { - } - argv_t& operator=(argv_t && lhs) noexcept - { - argv_t tmp(std::move(*this)); - argc_ = boost::exchange(lhs.argc_, 0); - argv_ = boost::exchange(lhs.argv_, nullptr); - reserved_ = boost::exchange(lhs.reserved_, 0); - return *this; - } - BOOST_PROCESS_V2_DECL ~argv_t(); - private: - BOOST_PROCESS_V2_DECL static - argv_t parse_(basic_cstring_ref input, error_code & ec); - friend struct shell; - int argc_ = 0; - char_type ** argv_ = nullptr; - int reserved_ = 0; - }; - - argv_t parse() const + shell(const shell &) = delete; + shell& operator=(const shell &) = delete; + + shell(shell && lhs) noexcept + : buffer_(std::move(lhs.buffer_)), + input_(std::move(lhs.input_)), + argc_(boost::exchange(lhs.argc_, 0)), + argv_(boost::exchange(lhs.argv_, nullptr)), + reserved_(boost::exchange(lhs.reserved_, 0)) { - error_code ec; - auto tmp = parse(ec); - if (ec) - detail::throw_error(ec, "parse cmd_line"); - return tmp; } - - argv_t parse(error_code & ec) const noexcept + shell& operator=(shell && lhs) noexcept { - return argv_t::parse_(cmd_line(), ec); + shell tmp(std::move(*this)); + buffer_ = std::move(lhs.buffer_); + input_ = std::move(lhs.input_); + argc_ = boost::exchange(lhs.argc_, 0); + argv_ = boost::exchange(lhs.argv_, nullptr); + reserved_ = boost::exchange(lhs.reserved_, 0); + return *this; } - basic_cstring_ref cmd_line() const noexcept + // the length of the parsed shell, including the executable + int argc() const { return argc_; } + char_type** argv() const { return argv_; } + + char_type** begin() const {return argv();} + char_type** end() const {return argv() + argc();} + + bool empty() const {return argc() == 0;} + std::size_t size() const {return static_cast(argc()); } + /// Native representation of the arguments to be used - excluding the executable + BOOST_PROCESS_V2_DECL args_type args() const; + template + filesystem::path exe(Environment && env = environment::current()) const { - return buffer_.empty() ? input_ : buffer_.c_str(); + if (argc() == 0) + return ""; + else + return environment::find_executable(0[argv()], std::forward(env)); } + BOOST_PROCESS_V2_DECL ~shell(); private: + BOOST_PROCESS_V2_DECL void parse_(); + // storage in case we need a conversion std::basic_string buffer_; - basic_cstring_ref input_; + basic_cstring_ref input_{buffer_}; + // impl details + int argc_ = 0; + char_type ** argv_ = nullptr; + int reserved_ = 0; + }; BOOST_PROCESS_V2_END_NAMESPACE diff --git a/include/boost/process/v2/windows/default_launcher.hpp b/include/boost/process/v2/windows/default_launcher.hpp index aa8d47891..6b71202e4 100644 --- a/include/boost/process/v2/windows/default_launcher.hpp +++ b/include/boost/process/v2/windows/default_launcher.hpp @@ -398,6 +398,11 @@ struct default_launcher return build_command_line_impl(pt, args, *std::begin(args)); } + static std::wstring build_command_line(const filesystem::path & pt, const wchar_t * args) + { + return args; + } + }; From 011380c28a2e159ba40c3da7a67b7d91b6dd7037 Mon Sep 17 00:00:00 2001 From: Klemens Date: Fri, 2 Sep 2022 18:43:35 +0800 Subject: [PATCH 120/397] Shell(posix) fixes. --- include/boost/process/v2/impl/shell.ipp | 9 ++-- .../process/v2/posix/default_launcher.hpp | 5 +++ test/v2/shell.cpp | 41 +++++++++++-------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/include/boost/process/v2/impl/shell.ipp b/include/boost/process/v2/impl/shell.ipp index 48a29aed9..1870297bc 100644 --- a/include/boost/process/v2/impl/shell.ipp +++ b/include/boost/process/v2/impl/shell.ipp @@ -85,9 +85,8 @@ auto shell::args() const-> args_type #else -shell::parse_() +void shell::parse_() { - shell::argv_t res; wordexp_t we{}; auto cd = wordexp(input_.c_str(), &we, WRDE_NOCMD); @@ -99,8 +98,6 @@ shell::parse_() argv_ = we.we_wordv; reserved_ = static_cast(we.we_offs); } - - return res; } shell::~shell() @@ -120,11 +117,11 @@ auto shell::args() const -> args_type { if (argc() == 0) { - static char * helper = nullptr; + static const char * helper = nullptr; return &helper; } else - return argv() + 1; + return const_cast(argv()); } #endif diff --git a/include/boost/process/v2/posix/default_launcher.hpp b/include/boost/process/v2/posix/default_launcher.hpp index 908ab4f5c..a2bb3b6cf 100644 --- a/include/boost/process/v2/posix/default_launcher.hpp +++ b/include/boost/process/v2/posix/default_launcher.hpp @@ -485,6 +485,11 @@ struct default_launcher return argv_.data(); } + const char * const * build_argv_(const filesystem::path &, const char ** argv) + { + return argv; + } + template const char * const * build_argv_(const filesystem::path & pt, const Args & args, typename std::enable_if< diff --git a/test/v2/shell.cpp b/test/v2/shell.cpp index f29d3ca45..8818e1956 100755 --- a/test/v2/shell.cpp +++ b/test/v2/shell.cpp @@ -10,6 +10,8 @@ // Test that header file is self-contained. #include +#include +#include #include @@ -25,27 +27,30 @@ BOOST_AUTO_TEST_CASE(test_shell_parser) { using boost::process::v2::shell; + namespace bpv = boost::process::v2; +#if defined(BOOST_PROCESS_V2_POSIX) + BOOST_CHECK_THROW(shell("foo \""), bpv::system_error); +#endif - auto sh = shell("foo \""); - boost::system::error_code ec; - auto argv = sh.parse(ec); + auto sh = shell(STR("foo bar \"foo bar\"")); + BOOST_CHECK(sh.argc() == 3u); + BOOST_CHECK(sh.argv()[0] == STR_VIEW("foo")); + BOOST_CHECK(sh.argv()[1] == STR_VIEW("bar")); + BOOST_CHECK(sh.argv()[2] == STR_VIEW("foo bar")); #if defined(BOOST_PROCESS_V2_POSIX) - for (auto s : sh.parse()) - { - std::wcout << s << std::endl; - } - BOOST_CHECK(argv.empty()); - BOOST_CHECK(ec); + auto raw_shell = "sh -c false"; +#else + auto raw_shell = "cmd /c exit 1"; #endif + sh = shell(raw_shell); + + auto exe = sh.exe(); + BOOST_CHECK(bpv::filesystem::exists(exe)); + + boost::asio::io_context ctx; + bpv::process proc{ctx, exe, sh.args()}; - sh = shell(STR("foo bar \"foo bar\"")); - - ec.clear(); - argv = sh.parse(ec); - BOOST_CHECK(argv.argc() == 3u); - BOOST_CHECK(!ec); - BOOST_CHECK(argv.argv()[0] == STR_VIEW("foo")); - BOOST_CHECK(argv.argv()[1] == STR_VIEW("bar")); - BOOST_CHECK(argv.argv()[2] == STR_VIEW("foo bar")); + proc.wait(); + BOOST_CHECK_EQUAL(proc.exit_code(), 1); } \ No newline at end of file From d36f4813925c14985ce2b62e326ee7a669680303 Mon Sep 17 00:00:00 2001 From: Klemens Morgenstern Date: Sat, 17 Sep 2022 20:39:01 +0800 Subject: [PATCH 121/397] Added WIN32_LEAN_AND_MEAN to cmake --- test/v2/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/v2/CMakeLists.txt b/test/v2/CMakeLists.txt index 4ee98e40e..51a1101eb 100644 --- a/test/v2/CMakeLists.txt +++ b/test/v2/CMakeLists.txt @@ -4,6 +4,9 @@ add_library(boost_process_v2_test_impl test_impl.cpp) target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process) target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1) +if (WIN32) + target_compile_definitions(boost_process_v2_test_impl PUBLIC WIN32_LEAN_AND_MEAN=1) +endif() function(boost_process_v2_standalone_test name) add_executable(boost_process_v2_${name} ${name}.cpp) From 7745fdc687f3307cc5dea95707c0ae76c05836f6 Mon Sep 17 00:00:00 2001 From: Klemens Date: Sun, 18 Sep 2022 17:56:47 +0800 Subject: [PATCH 122/397] Added code_as_error completion handler. --- include/boost/process/v2/detail/config.hpp | 8 +- include/boost/process/v2/exit_code.hpp | 140 +++++++++++++++++++++ test/v2/process.cpp | 24 ++++ 3 files changed, 168 insertions(+), 4 deletions(-) diff --git a/include/boost/process/v2/detail/config.hpp b/include/boost/process/v2/detail/config.hpp index 7e4fe58f4..9aa36e2fa 100644 --- a/include/boost/process/v2/detail/config.hpp +++ b/include/boost/process/v2/detail/config.hpp @@ -7,12 +7,12 @@ #if defined(BOOST_PROCESS_V2_STANDALONE) -#define BOOST_PROCESS_V2_ASIO_NAMESPACE ::asio +#define BOOST_PROCESS_V2_ASIO_NAMESPACE asio #define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) ASIO_COMPLETION_TOKEN_FOR(Sig) #define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) #define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature) #define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) ASIO_DEFAULT_COMPLETION_TOKEN(Executor) - +#define BOOST_PROCESS_V2_INITFN_DEDUCED_RESULT_TYPE(x,y,z) ASIO_INITFN_DEDUCED_RESULT_TYPE(x,y,z) #include #include @@ -39,12 +39,12 @@ #else -#define BOOST_PROCESS_V2_ASIO_NAMESPACE ::boost::asio +#define BOOST_PROCESS_V2_ASIO_NAMESPACE boost::asio #define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) BOOST_ASIO_COMPLETION_TOKEN_FOR(Sig) #define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) #define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature) #define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor) - +#define BOOST_PROCESS_V2_INITFN_DEDUCED_RESULT_TYPE(x,y,z) BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(x,y,z) #include #include diff --git a/include/boost/process/v2/exit_code.hpp b/include/boost/process/v2/exit_code.hpp index 5af4615b1..73ce81fc0 100644 --- a/include/boost/process/v2/exit_code.hpp +++ b/include/boost/process/v2/exit_code.hpp @@ -12,6 +12,15 @@ #define BOOST_PROCESS_V2_EXIT_CODE_HPP #include +#include + +#if defined(BOOST_PROCESS_V2_STANDALONE) +#include +#include +#else +#include +#include +#endif #if defined(BOOST_PROCESS_V2_POSIX) #include @@ -85,6 +94,137 @@ inline int evaluate_exit_code(int code) #endif + +/** Convert the exit-code in a completion into an error if the actual error isn't set. + * @code {.cpp} + * process proc{ctx, "exit", {"1"}}; + * + * proc.async_wait(code_as_error( + * [](error_code ec) + * { + * assert(ec.value() == 10); + * assert(ec.category() == error::get_exit_code_category()); + * })); + * + * @endcode + */ +template +struct code_as_error_t +{ + CompletionToken token_; + const error_category & category; + + template + code_as_error_t(Token_ && token, const error_category & category) + : token_(std::forward(token)), category(category) + { + } +}; + +/// Deduction function for code_as_error_t. +template +code_as_error_t code_as_error( + CompletionToken && token, + const error_category & category = error::get_exit_code_category()) +{ + return code_as_error_t::type>( + std::forward(token), category); +}; + +namespace detail +{ + +template +struct code_as_error_handler +{ + typedef void result_type; + + template + code_as_error_handler(H && h, const error_category & category) + : handler_(std::forward(h)), category(category) + { + } + + void operator()(error_code ec, native_exit_code_type code) + { + if (!ec) + ec.assign(code, category); + std::move(handler_)(ec); + } + + + Handler handler_; + const error_category & category; +}; + +} + + BOOST_PROCESS_V2_END_NAMESPACE +template +struct BOOST_PROCESS_V2_ASIO_NAMESPACE::async_result< + BOOST_PROCESS_V2_NAMESPACE::code_as_error_t, + void(BOOST_PROCESS_V2_NAMESPACE::error_code, + BOOST_PROCESS_V2_NAMESPACE::native_exit_code_type)> +{ + using signature = void(BOOST_PROCESS_V2_NAMESPACE::error_code); + + template + struct init_wrapper + { + init_wrapper(Initiation init) + : initiation_(std::move(init)) + { + } + + template + void operator()( + Handler && handler, + const BOOST_PROCESS_V2_NAMESPACE::error_category & cat, + Args && ... args) + { + std::move(initiation_)( + BOOST_PROCESS_V2_NAMESPACE::detail::code_as_error_handler::type>( + std::forward(handler), cat), + std::forward(args)...); + } + + Initiation initiation_; + + }; + + template + static BOOST_PROCESS_V2_INITFN_DEDUCED_RESULT_TYPE(CompletionToken, signature, + (async_initiate( + declval::type> >(), + declval(), + declval()...))) + initiate( + Initiation && initiation, + RawCompletionToken && token, + Args &&... args) + { + return async_initiate( + init_wrapper::type>( + std::forward(initiation)), + token.token_, + token.category, + std::forward(args)...); + } +}; + +template