2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 04:42:24 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Klemens Morgenstern
f9c13ff581 added check for ECHILD
Fixes #496
2025-06-21 00:54:14 +08:00
13 changed files with 66 additions and 170 deletions

View File

@@ -29,24 +29,3 @@ 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]
----

View File

@@ -100,10 +100,9 @@ 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 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))
// 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))
WaitHandler = 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>());
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>());
};
----

View File

@@ -26,7 +26,7 @@ in later version of C++.
asio::io_context ctx;
/// C++17
v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.err=nullptr});
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});
----

View File

@@ -42,30 +42,4 @@ 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[]
}
}

View File

@@ -1,28 +0,0 @@
// 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

View File

@@ -286,21 +286,24 @@ 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
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::bad_descriptor);
else if (process_is_running(exit_code))
ec = net::error::bad_descriptor;
else
{
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));
@@ -310,39 +313,38 @@ 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);
self.complete(ec, code);
}
};
net::dispatch(
net::get_associated_immediate_executor(self, descriptor.get_executor()),
completer{ec, std::move(self)});
net::post(descriptor.get_executor(), completer{ec, exit_code, std::move(self)});
}
template<typename Self>
void operator()(Self &&self, error_code ec, int = 0)
{
if (!ec && process_is_running(exit_code))
native_exit_code_type exit_code{};
if (!ec)
if (::waitpid(pid_, &exit_code, 0) == -1)
ec = get_last_error();
std::move(self).complete(ec);
std::move(self).complete(ec, exit_code);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, native_exit_code_type))
WaitHandler = net::default_completion_token_t<executor_type>>
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_))
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_))
{
return 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_);
}
};

View File

@@ -332,7 +332,6 @@ 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>
@@ -345,10 +344,11 @@ 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 if (process_is_running(exit_code))
else
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
@@ -391,19 +391,18 @@ 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);
self.complete(ec, code);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
WaitHandler = net::default_completion_token_t<executor_type>>
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_))
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_))
{
return 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_);
}
};
}

View File

@@ -308,8 +308,7 @@ 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)
{
@@ -327,11 +326,12 @@ struct basic_process_handle_signal
== 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 && process_is_running(exit_code))
else if (!ec)
{
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), ec));
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
}
#else
signal_set_dummy_ dummy_;
@@ -360,21 +360,20 @@ struct basic_process_handle_signal
}
#endif
template<typename Self>
void operator()(Self &&self, error_code ec)
void operator()(Self &&self, native_exit_code_type code, error_code ec)
{
self.complete(ec);
self.complete(ec, code);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
WaitHandler = net::default_completion_token_t<executor_type>>
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_))
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_))
{
return 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_);
}
};

View File

@@ -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 && process_is_running(exit_code)) // exit_code could be set by another call to wait.
if (!ec)
detail::get_exit_code_(handle.native_handle(), exit_code, ec);
std::move(self).complete(ec);
std::move(self).complete(ec, exit_code);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, native_exit_code_type))
WaitHandler = net::default_completion_token_t<executor_type>>
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_))
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_))
{
return 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_
);
}
};

View File

@@ -20,12 +20,10 @@
#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
@@ -302,8 +300,6 @@ 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);
@@ -318,8 +314,6 @@ 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)
@@ -361,22 +355,25 @@ private:
}
};
net::dispatch(
net::get_associated_immediate_executor(handle, handle.get_executor()),
completer{static_cast<int>(res), std::move(self)});
net::post(handle.get_executor(), completer{static_cast<int>(res), std::move(self)});
}
else
handle.async_wait(res, std::move(self));
handle.async_wait(std::move(self));
}
template<typename Self>
void operator()(Self && self, error_code ec)
void operator()(Self && self, error_code ec, native_exit_code_type code)
{
if (!ec && process_is_running(res))
handle.async_wait(res, std::move(self));
if (!ec && process_is_running(code))
handle.async_wait(std::move(self));
else
{
std::move(self).complete(ec, evaluate_exit_code(res));
if (!ec)
res = code;
else if (ec == boost::system::errc::no_child_process)
std::move(self).complete({}, evaluate_exit_code(res));
else
std::move(self).complete(ec, evaluate_exit_code(code));
}
}
};

View File

@@ -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{.err=nullptr});
* 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

View File

@@ -403,12 +403,7 @@ struct default_launcher
static std::wstring build_command_line(const filesystem::path & pt, const Args & args)
{
if (std::begin(args) == std::end(args))
{
std::wstring buffer;
buffer.resize(escaped_argv_length(pt.native()));
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
return buffer;
}
return pt.native();
return build_command_line_impl(pt, args, *std::begin(args));
}

View File

@@ -768,26 +768,6 @@ 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