mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd60cc1464 | ||
|
|
5597aa0055 | ||
|
|
31d6b5c9f8 | ||
|
|
f5c83eb9c5 | ||
|
|
224e3cf9aa | ||
|
|
bd450f9831 | ||
|
|
c72650df30 | ||
|
|
0c3c79672f | ||
|
|
a7e4fe99c1 | ||
|
|
f9fd327546 | ||
|
|
f372a9a119 | ||
|
|
921bd599b2 | ||
|
|
3999facdd3 | ||
|
|
0ea2eaed27 | ||
|
|
677d94f3a2 | ||
|
|
b8b8d27ac5 | ||
|
|
15555b9664 | ||
|
|
09555dac5f | ||
|
|
818e11672e | ||
|
|
06946018d6 |
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -23,19 +23,19 @@ jobs:
|
||||
include:
|
||||
- { toolset: gcc-5, cxxstd: "11,14,1z", os: ubuntu-latest, container: 'ubuntu:18.04', install: g++-5 }
|
||||
- { toolset: gcc-6, cxxstd: "11,14,1z", os: ubuntu-latest, container: 'ubuntu:18.04', install: g++-6 }
|
||||
- { toolset: gcc-7, cxxstd: "11,14,17", os: ubuntu-20.04, install: g++-7 }
|
||||
- { toolset: gcc-7, cxxstd: "11,14,17", os: ubuntu-latest, container: 'ubuntu:20.04', install: g++-7 }
|
||||
- { toolset: gcc-10, cxxstd: "11,14,17,2a", os: ubuntu-22.04, install: g++-10 }
|
||||
- { toolset: gcc-12, cxxstd: "11,14,17,20,2b", os: ubuntu-22.04, install: g++-12 }
|
||||
- { toolset: clang, compiler: clang++-3.9, cxxstd: "11,14", os: ubuntu-latest, container: 'ubuntu:18.04', install: clang-3.9 }
|
||||
- { toolset: clang, compiler: clang++-4.0, cxxstd: "11,14", os: ubuntu-latest, container: 'ubuntu:18.04', install: clang-4.0 }
|
||||
- { toolset: clang, compiler: clang++-5.0, cxxstd: "11,14,1z", os: ubuntu-latest, container: 'ubuntu:18.04', install: clang-5.0 }
|
||||
- { toolset: clang, compiler: clang++-6.0, cxxstd: "11,14,17", os: ubuntu-20.04, install: clang-6.0 }
|
||||
- { toolset: clang, compiler: clang++-7, cxxstd: "11,14,17", os: ubuntu-20.04, install: clang-7 }
|
||||
- { toolset: clang, compiler: clang++-8, cxxstd: "11,14,17", os: ubuntu-20.04, install: clang-8 }
|
||||
- { toolset: clang, compiler: clang++-9, cxxstd: "11,14,17,2a", os: ubuntu-20.04, install: clang-9 }
|
||||
- { toolset: clang, compiler: clang++-10, cxxstd: "11,14,17,2a", os: ubuntu-20.04, install: clang-10 }
|
||||
- { toolset: clang, compiler: clang++-11, cxxstd: "11,14,17,2a", os: ubuntu-20.04, install: clang-11 }
|
||||
- { toolset: clang, compiler: clang++-12, cxxstd: "11,14,17,2a", os: ubuntu-20.04, install: clang-12 }
|
||||
- { toolset: clang, compiler: clang++-6.0, cxxstd: "11,14,17", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-6.0 }
|
||||
- { toolset: clang, compiler: clang++-7, cxxstd: "11,14,17", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-7 }
|
||||
- { toolset: clang, compiler: clang++-8, cxxstd: "11,14,17", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-8 }
|
||||
- { toolset: clang, compiler: clang++-9, cxxstd: "11,14,17,2a", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-9 }
|
||||
- { toolset: clang, compiler: clang++-10, cxxstd: "11,14,17,2a", os: ubuntu-latest, container: 'ubuntu:20.04', install: clang-10 }
|
||||
- { toolset: clang, compiler: clang++-11, cxxstd: "11,14,17,2a", os: ubuntu-22.04, install: clang-11 }
|
||||
- { toolset: clang, compiler: clang++-12, cxxstd: "11,14,17,2a", os: ubuntu-22.04, install: clang-12 }
|
||||
- { toolset: clang, compiler: clang++-13, cxxstd: "11,14,17,20,2b", os: ubuntu-22.04, install: clang-13 }
|
||||
- { toolset: clang, compiler: clang++-14, cxxstd: "11,14,17,20,2b", os: ubuntu-22.04, install: clang-14 }
|
||||
- { toolset: clang, cxxstd: "11,14,17,2a", os: macos-13 }
|
||||
|
||||
@@ -8,6 +8,6 @@ A special thank you goes to [http://www.intra2net.com/(Intra2net AG) (especially
|
||||
|
||||
Great thanks also goes to Boris Schaeling, who despite having boost.process rejected, went on to work on it and maintained it up until this day and participated in the development of the current version.
|
||||
|
||||
Many Thanks, to [https://github.com/time-killer-games](Samuel Venable) for contributing the <<v2::ext>> functionality and all the research that went into it.
|
||||
Many Thanks, to [https://github.com/samuelvenable](Samuel Venable) for contributing the <<v2::ext>> functionality and all the research that went into it.
|
||||
|
||||
|
||||
|
||||
21
doc/env.adoc
21
doc/env.adoc
@@ -29,3 +29,24 @@ The subprocess environment assignment follows the same constraints:
|
||||
----
|
||||
include::../example/env.cpp[tag=subprocess_env]
|
||||
----
|
||||
|
||||
== Inheriting an environment
|
||||
|
||||
The current environment can be obtained by calling `environment::current` which returns
|
||||
a forward range of `environment::key_value_pair_view`.
|
||||
|
||||
.example/env.cpp:48-54
|
||||
[source,cpp,ident=0]
|
||||
----
|
||||
include::../example/env.cpp[tag=vector_env]
|
||||
----
|
||||
|
||||
Alternatively you can use a map container for the environment.
|
||||
|
||||
.example/env.cpp:61-68
|
||||
[source,cpp,ident=0]
|
||||
----
|
||||
include::../example/env.cpp[tag=map_env]
|
||||
----
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
=== `environment`
|
||||
|
||||
The `environment` header provides facilities to maniuplate the current environment and set it for new processes.
|
||||
The `environment` header provides facilities to manipulate the current environment and set it for new processes.
|
||||
|
||||
|
||||
An environment is a a `range` of `T` fulfilling these requirements:
|
||||
@@ -22,7 +22,7 @@ namespace environment
|
||||
// A char traits type that reflects the OS rules for string representing environment keys.
|
||||
/* Can be an alias of std::char_traits. May only be defined for `char` and `wchar_t`.
|
||||
*
|
||||
* Windows treats keys as case-insensitive yet perserving. The char traits are made to reflect
|
||||
* Windows treats keys as case-insensitive yet preserving. The char traits are made to reflect
|
||||
* that behaviour.
|
||||
*/
|
||||
template<typename Char>
|
||||
|
||||
@@ -100,9 +100,10 @@ struct basic_process_handle
|
||||
// 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_COMPLETION_TOKEN_FOR(void(error_code, native_exit_code_type))
|
||||
// Asynchronously wait for the process to exit and assign the native exit-code to the reference.
|
||||
// The exit_status can indicate that a process has already be waited for, e.g. when terminate is called.
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
|
||||
WaitHandler = net::default_completion_token_t<executor_type>>
|
||||
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>());
|
||||
auto async_wait(native_exit_code_type &exit_status, WaitHandler &&handler = net::default_completion_token_t<executor_type>());
|
||||
};
|
||||
----
|
||||
@@ -26,7 +26,7 @@ in later version of C++.
|
||||
asio::io_context ctx;
|
||||
|
||||
/// C++17
|
||||
v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.stderr=nullptr});
|
||||
v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.err=nullptr});
|
||||
/// C++11 & C++14
|
||||
v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{ {}, {}, nullptr});
|
||||
----
|
||||
|
||||
@@ -13,7 +13,7 @@ The major changes are
|
||||
* separate compilation
|
||||
* fd safe by default
|
||||
|
||||
Version 2 is now the defauled. In order to discourage usage of the deprecated v1, it's documentation has been removed.
|
||||
Version 2 is now the default. In order to discourage usage of the deprecated v1, it's documentation has been removed.
|
||||
|
||||
== Simplified Interface
|
||||
|
||||
|
||||
@@ -42,4 +42,30 @@ int main()
|
||||
process pro2(ctx, exe, {"test.cpp"}, process_environment(my_env));
|
||||
// end::subprocess_env[]
|
||||
}
|
||||
|
||||
{
|
||||
// tag::vector_env[]
|
||||
asio::io_context ctx;
|
||||
auto c = environment::current();
|
||||
// we need to use a value, since windows needs wchar_t.
|
||||
std::vector<environment::key_value_pair> my_env{c.begin(), c.end()};
|
||||
my_env.push_back("SECRET=THIS_IS_A_TEST");
|
||||
auto exe = environment::find_executable("g++", my_env);
|
||||
process proc(ctx, exe, {"main.cpp"}, process_environment(my_env));
|
||||
// end::vector_env[]
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
// tag::map_env[]
|
||||
asio::io_context ctx;
|
||||
std::unordered_map<environment::key, environment::value> my_env;
|
||||
for (const auto & kv : environment::current())
|
||||
if (kv.key().string() != "SECRET")
|
||||
my_env[kv.key()] = kv.value();
|
||||
|
||||
auto exe = environment::find_executable("g++", my_env);
|
||||
process proc(ctx, exe, {"main.cpp"}, process_environment(my_env));
|
||||
// end::map_env[]
|
||||
}
|
||||
}
|
||||
|
||||
28
include/boost/process/v1.hpp
Normal file
28
include/boost/process/v1.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2024 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_V1_HPP
|
||||
#define BOOST_PROCESS_V1_HPP
|
||||
|
||||
#include <boost/process/v1/args.hpp>
|
||||
#include <boost/process/v1/async.hpp>
|
||||
#include <boost/process/v1/async_system.hpp>
|
||||
#include <boost/process/v1/group.hpp>
|
||||
#include <boost/process/v1/child.hpp>
|
||||
#include <boost/process/v1/cmd.hpp>
|
||||
#include <boost/process/v1/env.hpp>
|
||||
#include <boost/process/v1/environment.hpp>
|
||||
#include <boost/process/v1/error.hpp>
|
||||
#include <boost/process/v1/exe.hpp>
|
||||
#include <boost/process/v1/group.hpp>
|
||||
#include <boost/process/v1/handles.hpp>
|
||||
#include <boost/process/v1/io.hpp>
|
||||
#include <boost/process/v1/pipe.hpp>
|
||||
#include <boost/process/v1/shell.hpp>
|
||||
#include <boost/process/v1/search_path.hpp>
|
||||
#include <boost/process/v1/spawn.hpp>
|
||||
#include <boost/process/v1/system.hpp>
|
||||
#include <boost/process/v1/start_dir.hpp>
|
||||
|
||||
#endif //BOOST_PROCESS_V1_HPP
|
||||
@@ -114,7 +114,7 @@ io_context ios;
|
||||
child c("ls", ios, on_exit=[](int exit, const std::error_code& ec_in){});
|
||||
|
||||
std::future<int> exit_code;
|
||||
chlid c2("ls", ios, on_exit=exit_code);
|
||||
child c2("ls", ios, on_exit=exit_code);
|
||||
|
||||
\endcode
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 {
|
||||
#if defined(BOOST_PROCESS_DOXYGEN)
|
||||
|
||||
|
||||
/** Class implementing an asnychronous I/O-Object for use with boost.asio.
|
||||
/** Class implementing an asynchronous I/O-Object for use with boost.asio.
|
||||
* It is based on the corresponding I/O Object, that is either boost::asio::windows::stream_handle or
|
||||
* boost::asio::posix::stream_descriptor.
|
||||
*
|
||||
|
||||
@@ -128,7 +128,7 @@ class child
|
||||
/** Same as valid, for convenience. */
|
||||
explicit operator bool() const;
|
||||
|
||||
/** Check if the the chlid process is in any process group. */
|
||||
/** Check if the the child process is in any process group. */
|
||||
bool in_group() const;
|
||||
|
||||
/** \overload bool in_group() const */
|
||||
|
||||
@@ -286,24 +286,21 @@ struct basic_process_handle_fd
|
||||
{
|
||||
net::posix::basic_descriptor<Executor> &descriptor;
|
||||
pid_type pid_;
|
||||
|
||||
native_exit_code_type & exit_code;
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
self.reset_cancellation_state(asio::enable_total_cancellation());
|
||||
error_code ec;
|
||||
native_exit_code_type exit_code{};
|
||||
int wait_res = -1;
|
||||
if (pid_ <= 0) // error, complete early
|
||||
ec = net::error::bad_descriptor;
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::bad_descriptor);
|
||||
else if (process_is_running(exit_code))
|
||||
{
|
||||
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
|
||||
if (wait_res == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
|
||||
if (!ec && (wait_res == 0))
|
||||
{
|
||||
descriptor.async_wait(net::posix::descriptor_base::wait_read, std::move(self));
|
||||
@@ -313,38 +310,39 @@ struct basic_process_handle_fd
|
||||
struct completer
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type code;
|
||||
typename std::decay<Self>::type self;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.complete(ec, code);
|
||||
self.complete(ec);
|
||||
}
|
||||
};
|
||||
net::post(descriptor.get_executor(), completer{ec, exit_code, std::move(self)});
|
||||
net::dispatch(
|
||||
net::get_associated_immediate_executor(self, descriptor.get_executor()),
|
||||
completer{ec, std::move(self)});
|
||||
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec, int = 0)
|
||||
{
|
||||
native_exit_code_type exit_code{};
|
||||
if (!ec)
|
||||
if (!ec && process_is_running(exit_code))
|
||||
if (::waitpid(pid_, &exit_code, 0) == -1)
|
||||
ec = get_last_error();
|
||||
std::move(self).complete(ec, exit_code);
|
||||
std::move(self).complete(ec);
|
||||
}
|
||||
};
|
||||
public:
|
||||
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, native_exit_code_type))
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
|
||||
WaitHandler = net::default_completion_token_t<executor_type>>
|
||||
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{descriptor_, pid_}, handler, descriptor_))
|
||||
auto async_wait(native_exit_code_type & exit_code,
|
||||
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
|
||||
async_wait_op_{descriptor_, pid_, exit_code}, handler, descriptor_))
|
||||
{
|
||||
return net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{descriptor_, pid_}, handler, descriptor_);
|
||||
return net::async_compose<WaitHandler, void(error_code)>(
|
||||
async_wait_op_{descriptor_, pid_, exit_code}, handler, descriptor_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -332,6 +332,7 @@ struct basic_process_handle_fd_or_signal
|
||||
int dummy;
|
||||
#endif
|
||||
pid_type pid_;
|
||||
native_exit_code_type & exit_code;
|
||||
bool needs_post = true;
|
||||
|
||||
template<typename Self>
|
||||
@@ -344,11 +345,10 @@ struct basic_process_handle_fd_or_signal
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec, int = 0)
|
||||
{
|
||||
native_exit_code_type exit_code{};
|
||||
int wait_res = -1;
|
||||
if (pid_ <= 0) // error, complete early
|
||||
ec = net::error::bad_descriptor;
|
||||
else
|
||||
else if (process_is_running(exit_code))
|
||||
{
|
||||
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
|
||||
if (wait_res == -1)
|
||||
@@ -391,18 +391,19 @@ struct basic_process_handle_fd_or_signal
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, native_exit_code_type code, error_code ec)
|
||||
{
|
||||
self.complete(ec, code);
|
||||
self.complete(ec);
|
||||
}
|
||||
};
|
||||
public:
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
|
||||
WaitHandler = net::default_completion_token_t<executor_type>>
|
||||
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{descriptor_, signal_set_, pid_}, handler, descriptor_))
|
||||
auto async_wait(native_exit_code_type & exit_code,
|
||||
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
|
||||
async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_))
|
||||
{
|
||||
return net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{descriptor_, signal_set_, pid_}, handler, descriptor_);
|
||||
return net::async_compose<WaitHandler, void(error_code)>(
|
||||
async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -308,7 +308,8 @@ struct basic_process_handle_signal
|
||||
|
||||
net::basic_signal_set<Executor> &handle;
|
||||
pid_type pid_;
|
||||
|
||||
native_exit_code_type & exit_code;
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
@@ -319,19 +320,18 @@ struct basic_process_handle_signal
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec, int sig)
|
||||
void operator()(Self &&self, error_code ec, int /*sig*/)
|
||||
{
|
||||
if (ec == net::error::operation_aborted &&
|
||||
self.get_cancellation_state().cancelled()
|
||||
== net::cancellation_type::none)
|
||||
ec.clear();
|
||||
|
||||
native_exit_code_type exit_code = -1;
|
||||
int wait_res = -1;
|
||||
|
||||
if (pid_ <= 0) // error, complete early
|
||||
ec = net::error::bad_descriptor;
|
||||
else if (!ec)
|
||||
else if (!ec && process_is_running(exit_code))
|
||||
{
|
||||
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
|
||||
if (wait_res == -1)
|
||||
@@ -345,7 +345,7 @@ struct basic_process_handle_signal
|
||||
}
|
||||
|
||||
const auto exec = self.get_executor();
|
||||
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
|
||||
net::dispatch(exec, net::append(std::move(self), ec));
|
||||
}
|
||||
#else
|
||||
signal_set_dummy_ dummy_;
|
||||
@@ -360,20 +360,21 @@ struct basic_process_handle_signal
|
||||
}
|
||||
#endif
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, native_exit_code_type code, error_code ec)
|
||||
void operator()(Self &&self, error_code ec)
|
||||
{
|
||||
self.complete(ec, code);
|
||||
self.complete(ec);
|
||||
}
|
||||
};
|
||||
public:
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
|
||||
WaitHandler = net::default_completion_token_t<executor_type>>
|
||||
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{signal_set_, pid_}, handler, signal_set_))
|
||||
auto async_wait(native_exit_code_type & exit_code,
|
||||
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
|
||||
async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_))
|
||||
{
|
||||
return net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{signal_set_, pid_}, handler, signal_set_);
|
||||
return net::async_compose<WaitHandler, void(error_code)>(
|
||||
async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ struct basic_process_handle_win
|
||||
struct async_wait_op_
|
||||
{
|
||||
handle_type &handle;
|
||||
|
||||
native_exit_code_type & exit_code;
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
@@ -296,24 +296,24 @@ struct basic_process_handle_win
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec)
|
||||
{
|
||||
native_exit_code_type exit_code{};
|
||||
if (ec == asio::error::operation_aborted && !self.get_cancellation_state().cancelled())
|
||||
return handle.async_wait(std::move(self));
|
||||
|
||||
if (!ec)
|
||||
if (!ec && process_is_running(exit_code)) // exit_code could be set by another call to wait.
|
||||
detail::get_exit_code_(handle.native_handle(), exit_code, ec);
|
||||
std::move(self).complete(ec, exit_code);
|
||||
std::move(self).complete(ec);
|
||||
}
|
||||
};
|
||||
public:
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, native_exit_code_type))
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
|
||||
WaitHandler = net::default_completion_token_t<executor_type>>
|
||||
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{handle_}, handler, handle_))
|
||||
auto async_wait(native_exit_code_type & exit_code,
|
||||
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
|
||||
async_wait_op_{handle_, exit_code}, handler, handle_))
|
||||
{
|
||||
return net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{handle_}, handler, handle_
|
||||
return net::async_compose<WaitHandler, void(error_code)>(
|
||||
async_wait_op_{handle_, exit_code}, handler, handle_
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace environment
|
||||
/// A char traits type that reflects the OS rules for string representing environment keys.
|
||||
/** Can be an alias of std::char_traits. May only be defined for `char` and `wchar_t`.
|
||||
*
|
||||
* Windows treats keys as case-insensitive yet perserving. The char traits are made to reflect
|
||||
* Windows treats keys as case-insensitive yet preserving. The char traits are made to reflect
|
||||
* that behaviour.
|
||||
*/
|
||||
template<typename Char>
|
||||
@@ -1372,6 +1372,8 @@ struct current_view
|
||||
environment::native_iterator iterator_;
|
||||
};
|
||||
|
||||
using const_iterator = iterator;
|
||||
|
||||
iterator begin() const {return iterator(handle_.get());}
|
||||
iterator end() const {return iterator(detail::find_end(handle_.get()));}
|
||||
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <utility>
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/core/exchange.hpp>
|
||||
#endif
|
||||
@@ -300,6 +302,8 @@ struct basic_process
|
||||
*/
|
||||
bool running()
|
||||
{
|
||||
if (!process_is_running(exit_status_))
|
||||
return false;
|
||||
error_code ec;
|
||||
native_exit_code_type exit_code{};
|
||||
auto r = process_handle_.running(exit_code, ec);
|
||||
@@ -314,6 +318,8 @@ struct basic_process
|
||||
/// Throwing @overload bool running(error_code & ec)
|
||||
bool running(error_code & ec) noexcept
|
||||
{
|
||||
if (!process_is_running(exit_status_))
|
||||
return false;
|
||||
native_exit_code_type exit_code{};
|
||||
auto r = process_handle_.running(exit_code, ec);
|
||||
if (!ec && !r)
|
||||
@@ -355,23 +361,22 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
net::post(handle.get_executor(),
|
||||
completer{static_cast<int>(res), std::move(self)});
|
||||
net::dispatch(
|
||||
net::get_associated_immediate_executor(handle, handle.get_executor()),
|
||||
completer{static_cast<int>(res), std::move(self)});
|
||||
}
|
||||
else
|
||||
handle.async_wait(std::move(self));
|
||||
handle.async_wait(res, std::move(self));
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self && self, error_code ec, native_exit_code_type code)
|
||||
void operator()(Self && self, error_code ec)
|
||||
{
|
||||
if (!ec && process_is_running(code))
|
||||
handle.async_wait(std::move(self));
|
||||
if (!ec && process_is_running(res))
|
||||
handle.async_wait(res, std::move(self));
|
||||
else
|
||||
{
|
||||
if (!ec)
|
||||
res = code;
|
||||
std::move(self).complete(ec, evaluate_exit_code(code));
|
||||
std::move(self).complete(ec, evaluate_exit_code(res));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -130,8 +130,8 @@ struct process_io_binding
|
||||
|
||||
|
||||
template<typename ReadablePipe>
|
||||
process_io_binding(ReadablePipe & readable_pipe,
|
||||
typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STDIN_FILENO>::type * = nullptr)
|
||||
process_io_binding(ReadablePipe & pipe,
|
||||
typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STD_INPUT_HANDLE>::type * = nullptr)
|
||||
{
|
||||
net::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
@@ -145,8 +145,8 @@ struct process_io_binding
|
||||
|
||||
|
||||
template<typename WritablePipe>
|
||||
process_io_binding(WritablePipe & writable_pipe,
|
||||
typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STDIN_FILENO>::type * = nullptr)
|
||||
process_io_binding(WritablePipe & pipe,
|
||||
typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STD_INPUT_HANDLE>::type * = nullptr)
|
||||
{
|
||||
net::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
@@ -267,7 +267,7 @@ struct process_io_binding
|
||||
return ec;
|
||||
}
|
||||
|
||||
error_code on_exec_setup(posix::default_launcher & launcher,
|
||||
error_code on_exec_setup(posix::default_launcher &,
|
||||
const filesystem::path &, const char * const *)
|
||||
{
|
||||
if (::dup2(fd, target) == -1)
|
||||
@@ -308,7 +308,7 @@ typedef process_io_binding<STDERR_FILENO> process_error_binding;
|
||||
* * @code {.cpp}
|
||||
* asio::io_context ctx;
|
||||
* /// C++17
|
||||
* v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.stderr=nullptr});
|
||||
* v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.err=nullptr});
|
||||
* /// C++11 & C++14
|
||||
* v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{ {}, {}, nullptr});
|
||||
* stdin ^ ^ stderr
|
||||
|
||||
@@ -109,7 +109,7 @@ inline std::false_type probe_on_error(
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline auto probe_on_error(Launcher & launcher, Init && init, derived && )
|
||||
-> std::is_same<error_code, decltype(init.on_error(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>(), std::declval<std::error_code&>()))>;
|
||||
-> std::is_same<error_code, decltype(init.on_error(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>(), std::declval<error_code&>()))>;
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
using has_on_error = decltype(probe_on_error(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
|
||||
@@ -403,7 +403,12 @@ struct default_launcher
|
||||
static std::wstring build_command_line(const filesystem::path & pt, const Args & args)
|
||||
{
|
||||
if (std::begin(args) == std::end(args))
|
||||
return pt.native();
|
||||
{
|
||||
std::wstring buffer;
|
||||
buffer.resize(escaped_argv_length(pt.native()));
|
||||
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return build_command_line_impl(pt, args, *std::begin(args));
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec)
|
||||
|
||||
std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec)
|
||||
{
|
||||
if (size == 0u)
|
||||
return 0u;
|
||||
auto res = ::MultiByteToWideChar(
|
||||
CP_UTF8, // CodePage
|
||||
0, // dwFlags
|
||||
@@ -88,6 +90,8 @@ std::size_t convert_to_utf8(const wchar_t *in, std::size_t size, char * out,
|
||||
std::size_t convert_to_wide(const char *in, std::size_t size, wchar_t * out,
|
||||
std::size_t max_size, error_code & ec)
|
||||
{
|
||||
if (size == 0u)
|
||||
return 0u;
|
||||
auto res = ::MultiByteToWideChar(
|
||||
CP_UTF8, // CodePage
|
||||
0, // dwFlags
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(__linux__) || defined(__ANDROID__))
|
||||
#if (defined(__linux__) || defined(__ANDROID__) || defined(__gnu_hurd__))
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
@@ -211,7 +211,7 @@ shell cmd(boost::process::v2::pid_type pid, error_code & ec)
|
||||
return make_cmd_shell_::make(std::move(procargs), argc, argv.release(), fr_func);
|
||||
}
|
||||
|
||||
#elif (defined(__linux__) || defined(__ANDROID__))
|
||||
#elif (defined(__linux__) || defined(__ANDROID__) || defined(__gnu_hurd__))
|
||||
|
||||
shell cmd(boost::process::v2::pid_type pid, error_code & ec)
|
||||
{
|
||||
@@ -403,10 +403,10 @@ shell cmd(boost::process::v2::pid_type pid, error_code & ec)
|
||||
}
|
||||
|
||||
#else
|
||||
filesystem::path cmd(boost::process::v2::pid_type, error_code & ec)
|
||||
shell cmd(boost::process::v2::pid_type, error_code & ec)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ENOTSUP, system_category());
|
||||
return "";
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ env_view env(boost::process::v2::pid_type pid, error_code & ec)
|
||||
return {};
|
||||
}
|
||||
|
||||
#elif (defined(__APPLE___) || defined(__MACH__)) && !TARGET_OS_IOS
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) && !TARGET_OS_IOS
|
||||
|
||||
env_view env(boost::process::v2::pid_type pid, error_code & ec)
|
||||
{
|
||||
@@ -309,7 +309,7 @@ env_view env(boost::process::v2::pid_type pid, error_code & ec)
|
||||
return ev;
|
||||
}
|
||||
|
||||
#elif (defined(__linux__) || defined(__ANDROID__))
|
||||
#elif (defined(__linux__) || defined(__ANDROID__)) || defined(__gnu_hurd__)
|
||||
|
||||
env_view env(boost::process::v2::pid_type pid, error_code & ec)
|
||||
{
|
||||
|
||||
10
src/pid.cpp
10
src/pid.cpp
@@ -179,7 +179,6 @@ std::vector<pid_type> child_pids(pid_type pid, error_code & ec)
|
||||
{
|
||||
std::vector<pid_type> vec;
|
||||
#if defined(PROC_PPID_ONLY)
|
||||
|
||||
vec.resize(proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, nullptr, 0) / sizeof(pid_type));
|
||||
const auto sz = proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, &vec[0], sizeof(pid_type) * vec.size());
|
||||
if (sz < 0)
|
||||
@@ -189,7 +188,14 @@ std::vector<pid_type> child_pids(pid_type pid, error_code & ec)
|
||||
}
|
||||
vec.resize(sz);
|
||||
#else
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ENOTSUP, system_category());
|
||||
std::vector<pid_type> pids = all_pids(ec);
|
||||
for (std::size_t i = 0; i < pids.size(); i++)
|
||||
{
|
||||
if (pid == parent_pid(pids[i], ec))
|
||||
{
|
||||
vec.push_back(pids[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return vec;
|
||||
}
|
||||
|
||||
@@ -245,6 +245,67 @@ BOOST_AUTO_TEST_CASE(print_args_out)
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_args_spec_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", "\"", "#foobar"}, 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);
|
||||
while (ec == asio::error::interrupted)
|
||||
sz += asio::read(rp, st, ec);
|
||||
|
||||
BOOST_CHECK_NE(sz, 0u);
|
||||
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("&", 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("\"", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("#foobar", line);
|
||||
|
||||
|
||||
proc.wait();
|
||||
BOOST_CHECK(proc.exit_code() == 0);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_args_err)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
@@ -768,6 +829,26 @@ BOOST_AUTO_TEST_CASE(no_zombie)
|
||||
BOOST_CHECK_EQUAL(errno, ECHILD);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_terminate_code)
|
||||
{
|
||||
asio::io_context ctx;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
bpv::process proc(ctx, pth, {"sleep", "1000"});
|
||||
|
||||
proc.async_wait([&](boost::system::error_code ec, int code)
|
||||
{
|
||||
BOOST_CHECK_MESSAGE(!ec, ec.what());
|
||||
BOOST_CHECK_EQUAL(code, SIGKILL);
|
||||
BOOST_CHECK(!proc.running());
|
||||
});
|
||||
|
||||
asio::post(ctx, [&]{proc.terminate();});
|
||||
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ int main(int argc, char * argv[])
|
||||
{
|
||||
if (kind == CTRL_CLOSE_EVENT)
|
||||
{
|
||||
// windows doesn't like us doing antyhing else
|
||||
// windows doesn't like us doing anything else
|
||||
::exit(0);
|
||||
if (tim_p != nullptr)
|
||||
tim_p->cancel();
|
||||
|
||||
Reference in New Issue
Block a user