mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 16:52:14 +00:00
Compare commits
51 Commits
boost-1.88
...
issue/491-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11faf28fed | ||
|
|
2a41d0a0dc | ||
|
|
dc00bf81d6 | ||
|
|
47b5c3c191 | ||
|
|
60affa362c | ||
|
|
aa40c138ed | ||
|
|
c5986d7f57 | ||
|
|
1e572e1756 | ||
|
|
635c226066 | ||
|
|
773ac747d5 | ||
|
|
322f581d1f | ||
|
|
9df0ee099b | ||
|
|
01c9a5b60f | ||
|
|
ed7099687a | ||
|
|
7fb5049feb | ||
|
|
02e14e8fff | ||
|
|
484d6e7a90 | ||
|
|
878a9e6ee9 | ||
|
|
1bfe21baa3 | ||
|
|
1765cd57bb | ||
|
|
7212471b57 | ||
|
|
5597aa0055 | ||
|
|
31d6b5c9f8 | ||
|
|
f5c83eb9c5 | ||
|
|
224e3cf9aa | ||
|
|
bd450f9831 | ||
|
|
c72650df30 | ||
|
|
0c3c79672f | ||
|
|
a7e4fe99c1 | ||
|
|
f9fd327546 | ||
|
|
f372a9a119 | ||
|
|
921bd599b2 | ||
|
|
3999facdd3 | ||
|
|
0ea2eaed27 | ||
|
|
677d94f3a2 | ||
|
|
b8b8d27ac5 | ||
|
|
15555b9664 | ||
|
|
09555dac5f | ||
|
|
818e11672e | ||
|
|
06946018d6 | ||
|
|
cd1621b197 | ||
|
|
1baccf76cd | ||
|
|
06595a2070 | ||
|
|
7e712985c1 | ||
|
|
da08060021 | ||
|
|
afdbab734e | ||
|
|
9dcd1a2775 | ||
|
|
5756891558 | ||
|
|
1d6c9ed0ec | ||
|
|
a941c8e89c | ||
|
|
89d2cc325a |
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 }
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.16)
|
||||
cmake_minimum_required(VERSION 3.8...3.31)
|
||||
|
||||
project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||
|
||||
option(BOOST_PROCESS_USE_STD_FS "Use std::filesystem instead of Boost.Filesystem" OFF)
|
||||
|
||||
add_library(boost_process
|
||||
src/detail/environment_posix.cpp
|
||||
src/detail/environment_win.cpp
|
||||
@@ -49,13 +51,14 @@ target_compile_definitions(boost_process
|
||||
PRIVATE BOOST_PROCESS_SOURCE=1
|
||||
)
|
||||
|
||||
if (BOOST_PROCESS_USE_STD_FS)
|
||||
target_compile_definitions(boost_process PUBLIC BOOST_PROCESS_USE_STD_FS=1 )
|
||||
if(BOOST_PROCESS_USE_STD_FS)
|
||||
target_compile_definitions(boost_process PUBLIC BOOST_PROCESS_USE_STD_FS)
|
||||
target_compile_features(boost_process PUBLIC cxx_std_17)
|
||||
else()
|
||||
target_link_libraries(boost_process PUBLIC Boost::filesystem)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if(WIN32)
|
||||
target_link_libraries(boost_process PUBLIC ntdll shell32 advapi32 user32 ws2_32)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ Boost.process is a library for comfortable management of processes, released wit
|
||||
|
||||
| Branches | Linux / Windows | Code coverage | Matrix |
|
||||
|----------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Develop: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](http://www.boost.org/development/tests/develop/developer/process.html) |
|
||||
| Master: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](http://www.boost.org/development/tests/master/developer/process.html) |
|
||||
| Develop: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](https://regression.boost.io/develop/developer/process.html) |
|
||||
| Master: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](https://regression.boost.io/master/developer/process.html) |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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});
|
||||
----
|
||||
@@ -35,12 +35,57 @@ 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 compatible 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.
|
||||
- any io-object with a `.native_handle()` function that is compatible with the above. E.g. a `asio::ip::tcp::socket`, or a pipe object.
|
||||
- a filesystem::path, which will open a readable or writable depending on the direction of the stream
|
||||
- an `asio::basic_writeable_pipe` for stdin or `asio::basic_readable_pipe` for stderr/stdout.
|
||||
|
||||
When passing a `FILE*`, a `native_handle` or an io-object with a `native_handle`,
|
||||
the initializer will assign the handle as is to the child process.
|
||||
That is the file descriptor/handle gets cloned into the subprocess and used without modification.
|
||||
|
||||
When passing a filesystem::path, the initializer will attempt to open the file and then pass the handle
|
||||
to the subprocess.
|
||||
|
||||
When passing a `readable_pipe` to stdout/stderr or a `writable_pipe` to stdin by reference,
|
||||
the initializer to create the other side of the pipe (`writable_pipe` for stdout/stderr, `readable_pipe` for `stdin`),
|
||||
connect the pair and pass the native_handle to the child process.
|
||||
|
||||
That is, these two are equivalent:
|
||||
|
||||
.Implicit construction of the readable pipe.
|
||||
[source,cpp]
|
||||
----
|
||||
asio::io_context ctx;
|
||||
asio::writable_pipe wp{ctx};
|
||||
// create a readable pipe internally and connect it to wp
|
||||
process proc{ctx, "/bin/bash", {}, process_stdio{.in=wp}};
|
||||
|
||||
// create it explicitly
|
||||
{
|
||||
// the pipe the child process reads from
|
||||
asio::readable_pipe rp{ctx};
|
||||
asio::connect_pipe(rp, wp);
|
||||
// `rp.native_handle()` will be assigned to the child processes stdin
|
||||
process proc{ctx, "/bin/bash", {}, process_stdio{.in=rp}};
|
||||
rp.close(); // close it so the pipe closes when the `proc exits.
|
||||
}
|
||||
----
|
||||
|
||||
The explicit version allows you to assign the same `writable_pipe` to `stdout` and `stderr`:
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
// the pipe the parent process reads from and both
|
||||
// stderr & stdout of the child process write to
|
||||
asio::readable_pipe rp{ctx};
|
||||
asio::writable_pipe wp{ctx};
|
||||
asio::connect_pipe(rp, wp);
|
||||
process proc{ctx, "/bin/bash", {}, process_stdio{.out=wp, .err=wp}};
|
||||
wp.close(); // close it so the pipe closes when the `proc exits.
|
||||
----
|
||||
|
||||
NOTE: If the child writes to a pipe, the parent reads from it et vice versa.
|
||||
|
||||
|
||||
[source,cpp]
|
||||
@@ -52,4 +97,4 @@ struct process_stdio
|
||||
__implementation_defined__ out;
|
||||
__implementation_defined__ err;
|
||||
};
|
||||
----
|
||||
----
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -31,6 +31,8 @@ inline std::string make_pipe_name()
|
||||
static std::atomic_size_t cnt{0};
|
||||
name += std::to_string(pid);
|
||||
name += "_";
|
||||
name += std::to_string(intptr_t(&cnt)); // to unclash Boost instances in plug-in DLLs
|
||||
name += "_";
|
||||
name += std::to_string(cnt++);
|
||||
|
||||
return name;
|
||||
@@ -60,8 +62,6 @@ public:
|
||||
async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink, const std::string & name)
|
||||
: async_pipe(ios_source, ios_sink, name, false) {}
|
||||
|
||||
|
||||
|
||||
inline async_pipe(const async_pipe& rhs);
|
||||
async_pipe(async_pipe&& rhs) : _source(std::move(rhs._source)), _sink(std::move(rhs._sink))
|
||||
{
|
||||
@@ -153,7 +153,6 @@ public:
|
||||
return _sink.write_some(buffers);
|
||||
}
|
||||
|
||||
|
||||
template<typename MutableBufferSequence>
|
||||
std::size_t read_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
|
||||
{
|
||||
@@ -281,7 +280,6 @@ async_pipe::async_pipe(const async_pipe& p) :
|
||||
_sink. assign(sink);
|
||||
}
|
||||
|
||||
|
||||
async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
boost::asio::io_context & ios_sink,
|
||||
const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink)
|
||||
@@ -298,7 +296,6 @@ async_pipe::async_pipe(boost::asio::io_context & ios_source,
|
||||
| FILE_FLAG_OVERLAPPED_, //write flag
|
||||
0, private_ ? 1 : ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
|
||||
|
||||
|
||||
if (source == boost::winapi::INVALID_HANDLE_VALUE_)
|
||||
::boost::process::v1::detail::throw_last_error("create_named_pipe(" + name + ") failed");
|
||||
|
||||
|
||||
@@ -162,7 +162,9 @@ struct exe_cmd_init : handler_base_ext
|
||||
}
|
||||
static exe_cmd_init<Char> exe_args_shell(string_type && exe, std::vector<string_type> && args)
|
||||
{
|
||||
std::vector<string_type> args_ = {c_arg(Char()), std::move(exe)};
|
||||
std::vector<string_type> args_ = {c_arg(Char())};
|
||||
if (!exe.empty())
|
||||
args_.emplace_back(std::move(exe));
|
||||
args_.insert(args_.end(), std::make_move_iterator(args.begin()), std::make_move_iterator(args.end()));
|
||||
string_type sh = get_shell(Char());
|
||||
|
||||
|
||||
@@ -78,8 +78,9 @@ inline std::locale default_locale()
|
||||
std::locale global_loc = std::locale();
|
||||
return std::locale(global_loc, new std::codecvt_utf8<wchar_t>);
|
||||
# else // Other POSIX
|
||||
// Return a default locale object.
|
||||
return std::locale();
|
||||
// ISO C calls std::locale("") "the locale-specific native environment", and this
|
||||
// locale is the default for many POSIX-based operating systems such as Linux.
|
||||
return std::locale("");
|
||||
# endif
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#else
|
||||
#if defined(BOOST_PROCESS_V2_PDFORK)
|
||||
#include <boost/process/v2/posix/pdfork_launcher.hpp>
|
||||
#elif defined(BOOST_PROCESS_V2_PIPEFORK)
|
||||
#include <boost/process/v2/posix/pipe_fork_launcher.hpp>
|
||||
#else
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
#endif
|
||||
@@ -48,6 +50,8 @@ typedef windows::default_launcher default_process_launcher;
|
||||
#else
|
||||
#if defined(BOOST_PROCESS_V2_PDFORK)
|
||||
typedef posix::pdfork_launcher default_process_launcher;
|
||||
#elif defined(BOOST_PROCESS_V2_PIPEFORK)
|
||||
typedef posix::pipe_fork_launcher default_process_launcher;
|
||||
#else
|
||||
typedef posix::default_launcher default_process_launcher;
|
||||
#endif
|
||||
|
||||
@@ -165,7 +165,7 @@ BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#if defined(SYS_pidfd_open)
|
||||
#if defined(SYS_pidfd_open) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN)
|
||||
#define BOOST_PROCESS_V2_PIDFD_OPEN 1
|
||||
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
|
||||
#endif
|
||||
|
||||
@@ -69,6 +69,8 @@ struct basic_process_handle_fd
|
||||
basic_process_handle_fd(executor_type executor, pid_type pid)
|
||||
: pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0))
|
||||
{
|
||||
if (descriptor_.native_handle() == -1)
|
||||
detail::throw_error(detail::get_last_error(), "wait(pid)");
|
||||
}
|
||||
|
||||
basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle)
|
||||
@@ -255,7 +257,7 @@ struct basic_process_handle_fd
|
||||
ec.clear();
|
||||
exit_code = code;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code)
|
||||
@@ -286,24 +288,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 +312,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_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -18,19 +18,27 @@
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/append.hpp>
|
||||
#include <asio/associated_immediate_executor.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/posix/basic_stream_descriptor.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/windows/signal_set.hpp>
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
#include <asio/signal_set.hpp>
|
||||
#endif
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/append.hpp>
|
||||
#include <boost/asio/associated_immediate_executor.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/posix/basic_stream_descriptor.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
@@ -45,7 +53,7 @@ struct basic_process_handle_fd_or_signal
|
||||
typedef Executor executor_type;
|
||||
|
||||
executor_type get_executor()
|
||||
{ return signal_set_.get_executor(); }
|
||||
{ return descriptor_.get_executor(); }
|
||||
|
||||
/// Rebinds the process_handle to another executor.
|
||||
template<typename Executor1>
|
||||
@@ -277,14 +285,13 @@ struct basic_process_handle_fd_or_signal
|
||||
int res = ::waitpid(pid_, &code, WNOHANG);
|
||||
if (res == -1)
|
||||
ec = get_last_error();
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
if (process_is_running(res))
|
||||
else if (res == 0)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
ec.clear();
|
||||
exit_code = code;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -311,13 +318,21 @@ struct basic_process_handle_fd_or_signal
|
||||
struct basic_process_handle_fd_or_signal;
|
||||
pid_type pid_ = -1;
|
||||
net::posix::basic_stream_descriptor<Executor> descriptor_;
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
net::basic_signal_set<Executor> signal_set_{descriptor_.get_executor(), SIGCHLD};
|
||||
|
||||
#else
|
||||
int signal_set_;
|
||||
#endif
|
||||
struct async_wait_op_
|
||||
{
|
||||
net::posix::basic_descriptor<Executor> &descriptor;
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
net::basic_signal_set<Executor> &handle;
|
||||
#else
|
||||
int dummy;
|
||||
#endif
|
||||
pid_type pid_;
|
||||
native_exit_code_type & exit_code;
|
||||
bool needs_post = true;
|
||||
|
||||
template<typename Self>
|
||||
@@ -330,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)
|
||||
@@ -343,46 +357,53 @@ struct basic_process_handle_fd_or_signal
|
||||
|
||||
if (!ec && (wait_res == 0))
|
||||
{
|
||||
needs_post = false;
|
||||
if (descriptor.is_open())
|
||||
descriptor.async_wait(
|
||||
net::posix::descriptor_base::wait_read,
|
||||
std::move(self));
|
||||
{
|
||||
needs_post = false;
|
||||
descriptor.async_wait(
|
||||
net::posix::descriptor_base::wait_read,
|
||||
std::move(self));
|
||||
return;
|
||||
}
|
||||
else
|
||||
handle.async_wait(std::move(self));
|
||||
return;
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
needs_post = false;
|
||||
handle.async_wait(std::move(self));
|
||||
return;
|
||||
#else
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
struct completer
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type code;
|
||||
typename std::decay<Self>::type self;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
|
||||
const auto exec = self.get_executor();
|
||||
completer cpl{ec, exit_code, std::move(self)};
|
||||
if (needs_post)
|
||||
net::post(exec, std::move(cpl));
|
||||
{
|
||||
auto exec = net::get_associated_immediate_executor(self, descriptor.get_executor());
|
||||
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
|
||||
}
|
||||
else
|
||||
net::dispatch(exec, std::move(cpl));
|
||||
|
||||
{
|
||||
auto exec = net::get_associated_executor(self);
|
||||
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
|
||||
}
|
||||
}
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, native_exit_code_type code, error_code ec)
|
||||
{
|
||||
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_);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,17 +17,25 @@
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/append.hpp>
|
||||
#include <asio/associated_immediate_executor.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
#include <asio/signal_set.hpp>
|
||||
#endif
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/append.hpp>
|
||||
#include <boost/asio/associated_immediate_executor.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
@@ -86,9 +94,14 @@ struct basic_process_handle_signal
|
||||
basic_process_handle_signal& operator=(basic_process_handle_signal && handle)
|
||||
{
|
||||
pid_ = handle.id();
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
|
||||
signal_set_.~basic_signal_set();
|
||||
using ss = net::basic_signal_set<Executor>;
|
||||
new (&signal_set_) ss(handle.get_executor(), SIGCHLD);
|
||||
#else
|
||||
signal_set_.executor = handle.signal_set_.executor;
|
||||
#endif
|
||||
handle.pid_ = -1;
|
||||
return *this;
|
||||
}
|
||||
@@ -244,11 +257,13 @@ struct basic_process_handle_signal
|
||||
int res = ::waitpid(pid_, &code, WNOHANG);
|
||||
if (res == -1)
|
||||
ec = get_last_error();
|
||||
|
||||
if (res == 0)
|
||||
else if (res == 0)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
ec.clear();
|
||||
exit_code = code;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -273,13 +288,28 @@ struct basic_process_handle_signal
|
||||
template<typename>
|
||||
friend struct basic_process_handle_signal;
|
||||
pid_type pid_ = -1;
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
net::basic_signal_set<Executor> signal_set_;
|
||||
|
||||
#else
|
||||
struct signal_set_dummy_
|
||||
{
|
||||
signal_set_dummy_(signal_set_dummy_ &&) = default;
|
||||
signal_set_dummy_(const signal_set_dummy_ &) = default;
|
||||
Executor executor;
|
||||
using executor_type = Executor;
|
||||
executor_type get_executor() {return executor;}
|
||||
signal_set_dummy_(Executor executor, int) : executor(std::move(executor)) {}
|
||||
};
|
||||
signal_set_dummy_ signal_set_;
|
||||
#endif
|
||||
struct async_wait_op_
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
|
||||
|
||||
net::basic_signal_set<Executor> &handle;
|
||||
pid_type pid_;
|
||||
|
||||
native_exit_code_type & exit_code;
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
@@ -290,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)
|
||||
@@ -315,31 +344,37 @@ struct basic_process_handle_signal
|
||||
return;
|
||||
}
|
||||
|
||||
struct completer
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type code;
|
||||
typename std::decay<Self>::type self;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
|
||||
const auto exec = self.get_executor();
|
||||
net::dispatch(exec, completer{ec, exit_code, std::move(self)});
|
||||
net::dispatch(exec, net::append(std::move(self), ec));
|
||||
}
|
||||
#else
|
||||
signal_set_dummy_ dummy_;
|
||||
pid_t pid;
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
auto exec = net::get_associated_immediate_executor(self, dummy_.get_executor());
|
||||
error_code ec;
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported);
|
||||
net::dispatch(exec, net::append(std::move(self), native_exit_code_type(), ec));
|
||||
}
|
||||
#endif
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec)
|
||||
{
|
||||
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_
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,9 +44,14 @@ std::basic_string<CharOut, Traits, Allocator> conv_string(
|
||||
if (ec)
|
||||
detail::throw_error(ec, "size_as_utf8");
|
||||
|
||||
|
||||
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
||||
res.resize(req_size);
|
||||
|
||||
if (req_size == 0)
|
||||
return res;
|
||||
|
||||
|
||||
auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "convert_to_utf8");
|
||||
@@ -70,6 +75,9 @@ std::basic_string<CharOut, Traits, Allocator> conv_string(
|
||||
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
||||
res.resize(req_size);
|
||||
|
||||
if (req_size == 0)
|
||||
return res;
|
||||
|
||||
auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "convert_to_wide");
|
||||
|
||||
@@ -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()));}
|
||||
|
||||
@@ -1760,9 +1762,12 @@ struct process_environment
|
||||
std::vector<environment::key_value_pair> env_buffer;
|
||||
std::vector<wchar_t> unicode_env;
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
error_code on_setup(windows::default_launcher & launcher,
|
||||
const filesystem::path &, const std::wstring &);
|
||||
const filesystem::path &, const std::wstring &)
|
||||
{
|
||||
return do_setup(launcher);
|
||||
}
|
||||
BOOST_PROCESS_V2_DECL error_code do_setup(windows::default_launcher & launcher);
|
||||
|
||||
#else
|
||||
|
||||
@@ -1811,7 +1816,13 @@ struct process_environment
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
error_code on_setup(posix::default_launcher & launcher,
|
||||
const filesystem::path &, const char * const *);
|
||||
const filesystem::path &, const char * const *)
|
||||
{
|
||||
return do_setup(launcher);
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_DECL error_code do_setup(posix::default_launcher & launcher);
|
||||
|
||||
|
||||
std::vector<environment::key_value_pair> env_buffer;
|
||||
std::vector<const char *> env;
|
||||
|
||||
@@ -352,9 +352,9 @@ struct default_launcher
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::fork();
|
||||
@@ -386,7 +386,7 @@ struct default_launcher
|
||||
ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
|
||||
@@ -115,7 +115,7 @@ struct fork_and_forget_launcher : default_launcher
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
|
||||
@@ -136,7 +136,7 @@ struct pdfork_launcher : default_launcher
|
||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
|
||||
185
include/boost/process/v2/posix/pipe_fork_launcher.hpp
Normal file
185
include/boost/process/v2/posix/pipe_fork_launcher.hpp
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
/// A launcher using `pipe_fork`. Default on FreeBSD
|
||||
struct pipe_fork_launcher : default_launcher
|
||||
{
|
||||
/// The file descriptor of the subprocess. Set after fork.
|
||||
pipe_fork_launcher() = default;
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pipe_fork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pipe_fork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
int fd = -1;
|
||||
{
|
||||
pipe_guard pg, pg_wait;
|
||||
if (::pipe(pg.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::pipe(pg_wait.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg_wait.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ec = detail::on_setup(*this, executable, argv, inits ...);
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
fd_whitelist.push_back(pg_wait.p[1]);
|
||||
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
::close(pg.p[0]);
|
||||
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
{
|
||||
close_all_fds(ec);
|
||||
}
|
||||
if (!ec)
|
||||
::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
|
||||
|
||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
::close(pg.p[1]);
|
||||
pg.p[1] = -1;
|
||||
::close(pg_wait.p[1]);
|
||||
pg_wait.p[1] = -1;
|
||||
int child_error{0};
|
||||
int count = -1;
|
||||
while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if ((err != EAGAIN) && (err != EINTR))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category());
|
||||
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
std::swap(fd, pg_wait.p[0]);
|
||||
}
|
||||
|
||||
basic_process<Executor> proc(exec, pid, fd);
|
||||
detail::on_success(*this, executable, argv, ec, inits...);
|
||||
return proc;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
@@ -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
|
||||
@@ -231,7 +233,7 @@ struct basic_process
|
||||
void resume()
|
||||
{
|
||||
error_code ec;
|
||||
suspend(ec);
|
||||
resume(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "resume");
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_PIDFD_OPEN)
|
||||
#include <boost/process/v2/detail/process_handle_fd.hpp>
|
||||
#elif defined(BOOST_PROCESS_V2_PDFORK)
|
||||
#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPEFORK)
|
||||
#include <boost/process/v2/detail/process_handle_fd_or_signal.hpp>
|
||||
#else
|
||||
// with asio support we could use EVFILT_PROC:NOTE_EXIT as well.
|
||||
@@ -107,9 +107,9 @@ struct basic_process_handle
|
||||
void request_exit()
|
||||
|
||||
/// Unconditionally terminates the process and stores the exit code in exit_status.
|
||||
void terminate(native_exit_code_type &exit_status, error_code &ec);\
|
||||
void terminate(native_exit_code_type &exit_status, error_code &ec);
|
||||
/// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
|
||||
void terminate(native_exit_code_type &exit_status);/
|
||||
void terminate(native_exit_code_type &exit_status);
|
||||
|
||||
/// Checks if the current process is running.
|
||||
/**If it has already completed, it assigns the exit code to `exit_code`.
|
||||
@@ -137,7 +137,7 @@ using basic_process_handle = detail::basic_process_handle_win<Executor>;
|
||||
#if defined(BOOST_PROCESS_V2_PIDFD_OPEN)
|
||||
template<typename Executor = net::any_io_executor>
|
||||
using basic_process_handle = detail::basic_process_handle_fd<Executor>;
|
||||
#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPE_LAUNCHER)
|
||||
#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPEFORK)
|
||||
template<typename Executor = net::any_io_executor>
|
||||
using basic_process_handle = detail::basic_process_handle_fd_or_signal<Executor>;
|
||||
#else
|
||||
|
||||
@@ -31,8 +31,33 @@
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct is_readable_pipe : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename Executor>
|
||||
struct is_readable_pipe<asio::basic_readable_pipe<Executor>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct is_writable_pipe : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename Executor>
|
||||
struct is_writable_pipe<asio::basic_writable_pipe<Executor>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
|
||||
struct handle_closer
|
||||
@@ -104,16 +129,10 @@ struct process_io_binding
|
||||
}
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(net::basic_readable_pipe<Executor> & pipe)
|
||||
template<typename ReadablePipe>
|
||||
process_io_binding(ReadablePipe & pipe,
|
||||
typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STD_INPUT_HANDLE>::type * = nullptr)
|
||||
{
|
||||
if (Target == STD_INPUT_HANDLE)
|
||||
{
|
||||
auto h_ = pipe.native_handle();
|
||||
h = std::unique_ptr<void, handle_closer>{h_, get_flags(h_)};
|
||||
return ;
|
||||
}
|
||||
|
||||
net::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
net::detail::create_pipe(p, ec);
|
||||
@@ -125,15 +144,10 @@ struct process_io_binding
|
||||
}
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(net::basic_writable_pipe<Executor> & pipe)
|
||||
template<typename WritablePipe>
|
||||
process_io_binding(WritablePipe & pipe,
|
||||
typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STD_INPUT_HANDLE>::type * = nullptr)
|
||||
{
|
||||
if (Target != STD_INPUT_HANDLE)
|
||||
{
|
||||
auto h_ = pipe.native_handle();
|
||||
h = std::unique_ptr<void, handle_closer>{h_, get_flags(h_)};
|
||||
return ;
|
||||
}
|
||||
net::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
net::detail::create_pipe(p, ec);
|
||||
@@ -170,7 +184,7 @@ struct process_io_binding
|
||||
process_io_binding & operator=(const process_io_binding &) = delete;
|
||||
|
||||
process_io_binding(process_io_binding && other) noexcept
|
||||
: fd(other.fd), fd_needs_closing(other.fd), ec(other.ec)
|
||||
: fd(other.fd), fd_needs_closing(other.fd_needs_closing), ec(other.ec)
|
||||
{
|
||||
other.fd = target;
|
||||
other.fd_needs_closing = false;
|
||||
@@ -207,15 +221,10 @@ struct process_io_binding
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(net::basic_readable_pipe<Executor> & readable_pipe)
|
||||
template<typename ReadablePipe>
|
||||
process_io_binding(ReadablePipe & readable_pipe,
|
||||
typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STDIN_FILENO>::type * = nullptr)
|
||||
{
|
||||
if (Target == STDIN_FILENO)
|
||||
{
|
||||
fd = readable_pipe.native_handle();
|
||||
return ;
|
||||
}
|
||||
|
||||
net::detail::native_pipe_handle p[2];
|
||||
net::detail::create_pipe(p, ec);
|
||||
if (ec)
|
||||
@@ -232,15 +241,10 @@ struct process_io_binding
|
||||
}
|
||||
|
||||
|
||||
template<typename Executor>
|
||||
process_io_binding(net::basic_writable_pipe<Executor> & writable_pipe)
|
||||
template<typename WritablePipe>
|
||||
process_io_binding(WritablePipe & writable_pipe,
|
||||
typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STDIN_FILENO>::type * = nullptr)
|
||||
{
|
||||
|
||||
if (Target != STDIN_FILENO)
|
||||
{
|
||||
fd = writable_pipe.native_handle();
|
||||
return ;
|
||||
}
|
||||
net::detail::native_pipe_handle p[2];
|
||||
error_code ec;
|
||||
net::detail::create_pipe(p, ec);
|
||||
@@ -263,7 +267,7 @@ struct process_io_binding
|
||||
return ec;
|
||||
}
|
||||
|
||||
error_code on_exec_setup(posix::default_launcher & launcher,
|
||||
error_code on_exec_setup(posix::default_launcher &,
|
||||
const filesystem::path &, const char * const *)
|
||||
{
|
||||
if (::dup2(fd, target) == -1)
|
||||
@@ -304,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
|
||||
|
||||
@@ -97,7 +97,7 @@ inline void invoke_on_error(Launcher & /*launcher*/, const filesystem::path &/*e
|
||||
template<typename Launcher, typename Init>
|
||||
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, executable, cmd_line, ec))
|
||||
-> decltype(init.on_error(launcher, executable, cmd_line, ec))
|
||||
{
|
||||
init.on_error(launcher, executable, cmd_line, ec);
|
||||
}
|
||||
@@ -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<void, 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{}));
|
||||
@@ -225,6 +225,9 @@ struct default_launcher
|
||||
INVALID_HANDLE_VALUE,
|
||||
INVALID_HANDLE_VALUE},
|
||||
nullptr};
|
||||
/// Allow batch files to be executed, which might pose a security threat.
|
||||
bool allow_batch_files = false;
|
||||
|
||||
/// The process_information that gets assigned after a call to CreateProcess
|
||||
PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
|
||||
|
||||
@@ -293,6 +296,12 @@ struct default_launcher
|
||||
Args && args,
|
||||
Inits && ... inits ) -> enable_init<Executor, Inits...>
|
||||
{
|
||||
if (!allow_batch_files && ((executable.extension() == ".bat") || (executable.extension() == ".cmd")))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_ACCESS_DENIED, system_category());
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
|
||||
auto command_line = this->build_command_line(executable, std::forward<Args>(args));
|
||||
|
||||
ec = detail::on_setup(*this, executable, command_line, inits...);
|
||||
@@ -352,7 +361,6 @@ struct default_launcher
|
||||
BOOST_PROCESS_V2_DECL static
|
||||
std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
|
||||
basic_string_view<wchar_t> ws);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -395,6 +403,7 @@ struct default_launcher
|
||||
{
|
||||
return detail::conv_string<wchar_t>(arg.data(), arg.size());
|
||||
});
|
||||
|
||||
return build_command_line_impl(pt, argw, L"");
|
||||
}
|
||||
|
||||
@@ -403,8 +412,14 @@ 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()));
|
||||
|
||||
if (!buffer.empty())
|
||||
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
|
||||
return buffer;
|
||||
}
|
||||
return build_command_line_impl(pt, args, *std::begin(args));
|
||||
}
|
||||
|
||||
@@ -433,4 +448,4 @@ BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|
||||
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="0; url=../../doc/html/process.html">
|
||||
<meta http-equiv="refresh" content="0; url=doc/html/index.html">
|
||||
<title>Boost.Process</title>
|
||||
<style>
|
||||
body {
|
||||
@@ -26,7 +26,7 @@
|
||||
<body>
|
||||
<p>
|
||||
Automatic redirection failed, please go to
|
||||
<a href="../../doc/html/process.html">../../doc/html/process.html</a>
|
||||
<a href="doc/html/index.html">doc/html/index.html</a>
|
||||
</p>
|
||||
<p>
|
||||
© 2016 Klemens D. Morgenstern
|
||||
|
||||
@@ -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
|
||||
@@ -210,10 +214,8 @@ std::size_t size_as_wide(const char * in, std::size_t size, error_code &)
|
||||
const auto from = in;
|
||||
const auto from_end = from + size;
|
||||
const char * from_next = from;
|
||||
std::size_t char_count = 0u;
|
||||
while (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<std::size_t>(from_end - from_next))
|
||||
|
||||
@@ -17,7 +17,7 @@ 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 &)
|
||||
error_code process_environment::do_setup(windows::default_launcher & launcher)
|
||||
{
|
||||
if (!unicode_env.empty() && !ec)
|
||||
{
|
||||
@@ -30,7 +30,7 @@ error_code process_environment::on_setup(windows::default_launcher & launcher, c
|
||||
|
||||
#else
|
||||
|
||||
error_code process_environment::on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||
error_code process_environment::do_setup(posix::default_launcher & launcher)
|
||||
{
|
||||
launcher.env = env.data();
|
||||
return error_code{};
|
||||
|
||||
@@ -100,7 +100,7 @@ struct exit_code_category final : public error_category
|
||||
# if defined(SIGILL)
|
||||
case SIGILL: return "SIGILL: Illegal Instruction";
|
||||
# endif
|
||||
# if defined(SIGINFO)
|
||||
# if defined(SIGINFO) && SIGINFO != SIGPWR
|
||||
case SIGINFO: return "SIGINFO: A synonym for SIGPWR";
|
||||
# endif
|
||||
# if defined(SIGINT)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
11
src/pid.cpp
11
src/pid.cpp
@@ -178,6 +178,7 @@ pid_type parent_pid(pid_type pid, error_code & ec)
|
||||
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)
|
||||
@@ -186,6 +187,16 @@ std::vector<pid_type> child_pids(pid_type pid, error_code & ec)
|
||||
return {};
|
||||
}
|
||||
vec.resize(sz);
|
||||
#else
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,54 +24,72 @@ namespace windows
|
||||
if (ws.empty())
|
||||
return 2u; // just quotes
|
||||
|
||||
constexpr static auto space = L' ';
|
||||
constexpr static auto quote = L'"';
|
||||
const auto needs_quotes = ws.find_first_of(L" \t") != basic_string_view<wchar_t>::npos;
|
||||
|
||||
const auto has_space = ws.find(space) != basic_string_view<wchar_t>::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 needed_escapes = 0u;
|
||||
for (auto itr = ws.begin(); itr != ws.end(); itr ++)
|
||||
{
|
||||
if (*itr == quote)
|
||||
needed_escapes++;
|
||||
else if (*itr == L'\\')
|
||||
{
|
||||
auto nx = std::next(itr);
|
||||
if (nx != ws.end() && *nx == L'"')
|
||||
needed_escapes ++;
|
||||
else if (nx == ws.end())
|
||||
needed_escapes ++;
|
||||
}
|
||||
}
|
||||
|
||||
return ws.size() + needed_escapes + (needs_quotes ? 2u : 0u);
|
||||
}
|
||||
|
||||
|
||||
std::size_t default_launcher::escape_argv_string(wchar_t * itr, std::size_t max_size,
|
||||
basic_string_view<wchar_t> ws)
|
||||
{
|
||||
const auto sz = escaped_argv_length(ws);
|
||||
constexpr static auto quote = L'"';
|
||||
const auto needs_quotes = ws.find_first_of(L" \t") != basic_string_view<wchar_t>::npos;
|
||||
const auto needed_escapes = std::count(ws.begin(), ws.end(), quote);
|
||||
|
||||
const auto sz = ws.size() + needed_escapes + (needs_quotes ? 2u : 0u);
|
||||
|
||||
if (sz > max_size)
|
||||
return 0u;
|
||||
|
||||
if (ws.empty())
|
||||
{
|
||||
itr[0] = L'"';
|
||||
itr[1] = L'"';
|
||||
itr[0] = quote;
|
||||
itr[1] = quote;
|
||||
return 2u;
|
||||
}
|
||||
|
||||
const auto has_space = ws.find(L' ') != basic_string_view<wchar_t>::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 (needs_quotes)
|
||||
*(itr++) = quote;
|
||||
|
||||
for (auto it = ws.begin(); it != ws.end(); it ++)
|
||||
{
|
||||
if (wc == L'"')
|
||||
if (*it == quote) // makes it \"
|
||||
*(itr++) = L'\\';
|
||||
*(itr++) = wc;
|
||||
|
||||
if (*it == L'\\') // \" needs to become \\\"
|
||||
{
|
||||
auto nx = std::next(it);
|
||||
if (nx != ws.end() && *nx == L'"')
|
||||
*(itr++) = L'\\';
|
||||
else if (nx == ws.end())
|
||||
*(itr++) = L'\\';
|
||||
|
||||
}
|
||||
|
||||
*(itr++) = *it;
|
||||
}
|
||||
|
||||
*(itr ++) = L'"';
|
||||
if (needs_quotes)
|
||||
*(itr++) = quote;
|
||||
return itr - begin;
|
||||
}
|
||||
|
||||
@@ -108,13 +126,18 @@ namespace windows
|
||||
auto tl = get_thread_attribute_list(ec);
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
auto itr = std::unique(inherited_handles.begin(), inherited_handles.end());
|
||||
auto size = std::distance(inherited_handles.begin(), itr);
|
||||
|
||||
if (!::UpdateProcThreadAttribute(
|
||||
tl, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
||||
inherited_handles.data(), inherited_handles.size() * sizeof(HANDLE), nullptr, nullptr))
|
||||
inherited_handles.data(), size * sizeof(HANDLE), nullptr, nullptr))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
}
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
enable_testing()
|
||||
|
||||
add_library(boost_process_v2_test_impl test_impl.cpp)
|
||||
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)
|
||||
target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process Ntdll)
|
||||
else()
|
||||
target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process)
|
||||
endif()
|
||||
|
||||
function(boost_process_v2_standalone_test name)
|
||||
add_executable(boost_process_v2_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
add_executable(boost_process_v2_${name} ${name}.cpp test_impl.cpp)
|
||||
target_link_libraries(boost_process_v2_${name} PUBLIC Boost::process Boost::system Boost::filesystem Boost::unit_test_framework )
|
||||
if (WIN32)
|
||||
target_compile_definitions(boost_process_v2_${name} PUBLIC WIN32_LEAN_AND_MEAN=1)
|
||||
target_link_libraries(boost_process_v2_${name} PUBLIC Ntdll)
|
||||
endif()
|
||||
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}> )
|
||||
endfunction()
|
||||
|
||||
@@ -27,13 +21,16 @@ target_link_libraries(boost_process_v2_test_target PUBLIC Boost::process Boost::
|
||||
|
||||
function(boost_process_v2_test_with_target name)
|
||||
add_executable(boost_process_v2_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
target_link_libraries(boost_process_v2_${name} PUBLIC Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
if (WIN32)
|
||||
target_compile_definitions(boost_process_v2_${name} PUBLIC WIN32_LEAN_AND_MEAN=1)
|
||||
target_link_libraries(boost_process_v2_${name} PUBLIC Ntdll)
|
||||
endif()
|
||||
add_dependencies(boost_process_v2_${name} boost_process_v2_test_target)
|
||||
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}>
|
||||
-- $<TARGET_FILE:boost_process_v2_test_target>)
|
||||
|
||||
|
||||
endfunction()
|
||||
|
||||
boost_process_v2_test_with_target(process)
|
||||
boost_process_v2_test_with_target(ext)
|
||||
boost_process_v2_test_with_target(ext)
|
||||
|
||||
@@ -33,7 +33,6 @@ project : requirements
|
||||
<os>NT,<toolset>cw:<library>ws2_32
|
||||
<os>NT,<toolset>gcc:<library>ws2_32
|
||||
<os>NT,<toolset>gcc:<library>Bcrypt
|
||||
<define>BOOST_PROCESS_V2_SEPARATE_COMPILATION=1
|
||||
<library>/boost/test//included
|
||||
;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
#include <boost/process/v2/process.hpp>
|
||||
#include <boost/process/v2/start_dir.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ext)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
#include <boost/process/v2/process.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -245,6 +245,83 @@ 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);
|
||||
|
||||
fprintf(stderr, "print_args_spec_out\n");
|
||||
|
||||
bpv::process proc(ctx, pth, {"print-args", "&foo", "&", "", "\"\"", "\\\"", "|bar", "\"", "#foobar"},
|
||||
bpv::process_stdio{/*in*/{},/*out*/wp, /*err*/ nullptr});
|
||||
BOOST_CHECK(proc.running());
|
||||
|
||||
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("", 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("\\\"", 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;
|
||||
@@ -725,7 +802,7 @@ BOOST_AUTO_TEST_CASE(async_cancel_wait)
|
||||
proc.async_wait(asio::cancel_after(std::chrono::milliseconds(100),
|
||||
[&](boost::system::error_code ec, int)
|
||||
{
|
||||
BOOST_CHECK(ec == asio::error::operation_aborted);
|
||||
BOOST_CHECK_EQUAL(ec, asio::error::operation_aborted);
|
||||
BOOST_CHECK(proc.running());
|
||||
if (proc.running())
|
||||
proc.terminate();
|
||||
@@ -768,8 +845,144 @@ 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
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_args_combined)
|
||||
{
|
||||
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= */ wp, /* .err=*/ wp});
|
||||
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(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("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("bar", 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("foo", line);
|
||||
|
||||
|
||||
proc.wait();
|
||||
BOOST_CHECK_EQUAL(proc.exit_code(), 0);
|
||||
}
|
||||
|
||||
|
||||
struct my_handler
|
||||
{
|
||||
boost::process::filesystem::path pt;
|
||||
bpv::error_code ec;
|
||||
|
||||
template<typename Launcher, typename CmdLine>
|
||||
bpv::error_code on_setup(Launcher &launcher, const bpv::filesystem::path& executable,
|
||||
CmdLine (&/*cmd_line*/))
|
||||
{
|
||||
pt = executable;
|
||||
if (executable == "/send/more/cops")
|
||||
return asio::error::no_recovery;
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Launcher, typename CmdLine>
|
||||
void on_error(Launcher &launcher, const bpv::filesystem::path& executable,
|
||||
CmdLine (&/*cmd_line*/), const bpv::error_code & ec)
|
||||
{
|
||||
this->ec = ec;
|
||||
}
|
||||
|
||||
template<typename Launcher, typename CmdLine>
|
||||
void on_success(Launcher &launcher, const bpv::filesystem::path& executable,
|
||||
CmdLine (&/*cmd_line*/))
|
||||
{
|
||||
ec.clear();
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(custom_handlers)
|
||||
{
|
||||
my_handler mh;
|
||||
|
||||
asio::io_context ctx;
|
||||
|
||||
BOOST_CHECK_THROW(bpv::process(ctx, "/send/more/cops", {}, mh), bpv::system_error);
|
||||
BOOST_CHECK_EQUAL(mh.ec, asio::error::no_recovery);
|
||||
|
||||
BOOST_CHECK_EQUAL(mh.pt, "/send/more/cops");
|
||||
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bpv::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
bpv::process proc(ctx, pth, {}, mh);
|
||||
|
||||
BOOST_CHECK_EQUAL(mh.pt, pth);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
|
||||
@@ -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