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

Compare commits

..

39 Commits

Author SHA1 Message Date
Klemens Morgenstern
2a41d0a0dc batch file execution is forbidden by default 2025-10-21 21:42:14 -07:00
Klemens Morgenstern
dc00bf81d6 Fixes args & inherited handles. 2025-10-21 21:41:11 -07:00
Klemens Morgenstern
47b5c3c191 [win] Added escaping for \ followed by space 2025-10-21 21:41:11 -07:00
Klemens Morgenstern
60affa362c Reworked arg handling on windows (v2) 2025-10-21 21:41:11 -07:00
Klemens Morgenstern
aa40c138ed Windows arg escape is handling internal quotes. 2025-10-21 21:41:11 -07:00
Klemens Morgenstern
c5986d7f57 Added test for combined stdout/stderr
Addresses #522.
2025-10-21 21:41:11 -07:00
Klemens Morgenstern
1e572e1756 Added more docs about pipes & process_stdio.
Closes #522.
2025-10-21 15:30:13 +08:00
Konvicka Filip
635c226066 Fix: corrected empty double quotes being added to cmd.exe /c on Windows with bp::shell and bp::args (caused by PR #256) 2025-10-21 14:46:50 +08:00
EelisVP
773ac747d5 Fix 'unused variable' warning 2025-10-21 10:58:45 +08:00
Klemens Morgenstern
322f581d1f Increased version range to 3.31 2025-10-09 23:38:18 +08:00
Klemens Morgenstern
9df0ee099b target_link_Libraries signature fix 2025-10-09 21:25:32 +08:00
Klemens Morgenstern
01c9a5b60f removed v2/test_impl target 2025-10-09 09:29:37 +08:00
Klemens Morgenstern
ed7099687a Removed filesystem::path from ABI
Closes #516.
2025-10-06 13:17:13 +08:00
chn
7fb5049feb fix typo in stdio move constructor 2025-10-06 12:49:38 +08:00
Klemens Morgenstern
02e14e8fff Fixed cmake for tests
Closes #515
2025-10-06 12:44:33 +08:00
Klemens Morgenstern
484d6e7a90 added test for special args to tests 2025-10-06 12:44:33 +08:00
Alexander Grund
878a9e6ee9 Fix required CMake version 2025-10-03 10:12:02 +08:00
Klemens Morgenstern
1bfe21baa3 Failed pidfd_open causes an exception
Closes #513.
2025-09-07 17:44:47 +08:00
Klemens Morgenstern
1765cd57bb Launchers use _exit instead of exit on error.
Closes #514
2025-09-07 17:44:47 +08:00
Alexander Grund
7212471b57 Update Link to regression test matrix in README 2025-09-07 17:30:30 +08:00
Klemens Morgenstern
5597aa0055 changed env example for windows wchar_t. 2025-07-01 18:59:19 +08:00
Klemens Morgenstern
31d6b5c9f8 process_handle.async_wait accepts ref to exit_code
Closes #503.
2025-07-01 18:59:19 +08:00
Klemens Morgenstern
f5c83eb9c5 wait checks the error code first.
See #499.
2025-07-01 18:59:19 +08:00
Klemens Morgenstern
224e3cf9aa [windows] fixed escaping of path without args.
Closes #501. Credit to @melak47 for the solution.
2025-07-01 18:59:19 +08:00
Klemens Morgenstern
bd450f9831 fixed wrong comment/doc using .stderr
Closes #500
2025-06-26 23:17:08 +08:00
Klemens Morgenstern
c72650df30 added example for modifying inherited environment. 2025-06-26 22:48:16 +08:00
Klemens Morgenstern
0c3c79672f added v1.hpp. 2025-06-26 22:14:16 +08:00
Klemens Morgenstern
a7e4fe99c1 stdio.hpp name fix. 2025-06-21 00:54:03 +08:00
Klemens Morgenstern
f9fd327546 added const_iterator alias to current_view.
Closes #497.
2025-06-21 00:37:03 +08:00
Klemens Morgenstern
f372a9a119 fixed MultiByteToWideChar use for empty strings
Closes #495
2025-06-21 00:25:10 +08:00
Klemens Morgenstern
921bd599b2 Fixed wrong type in probe_on_error on windows.
Closes #491.
2025-06-21 00:02:12 +08:00
Klemens Morgenstern
3999facdd3 unused variable fixes. 2025-06-21 00:00:22 +08:00
high on tantor
0ea2eaed27 Support child_pids() Even When PROC_PPID_ONLY is Undefined on Mac
Needs testing...
2025-06-20 23:59:27 +08:00
high on tantor
677d94f3a2 Correct Samuel Venable Link In Acknowledgements Document 2025-06-20 23:56:40 +08:00
Klemens Morgenstern
b8b8d27ac5 fix STD_INPUT_HANDLE issue in stdio.hpp 2025-06-20 23:56:01 +08:00
Dirk Stolle
15555b9664 Fix some typos 2025-05-06 10:11:41 +08:00
Petter Reinholdtsen
09555dac5f Add cmd & env support on GNU/Hurd 2025-05-05 22:32:56 +08:00
Petter Reinholdtsen
818e11672e Fix build on systems without cmd() implementation 2025-05-05 22:31:38 +08:00
Dirk Stolle
06946018d6 Remove Ubuntu 20.04 builds from CI
The Ubuntu 20.04 image on GitHub Actions has been unavailable
since 2025-04-15. See <https://github.com/actions/runner-images/issues/11101>
for more information on the deprecation and removal.

Therefore all build jobs that use the Ubuntu 20.04 runner image
of GHA will fail and have to be replaced by newer images or have
to move into Ubuntu 20.04 containers.
2025-05-05 22:30:26 +08:00
38 changed files with 517 additions and 162 deletions

View File

@@ -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 }

View File

@@ -3,7 +3,7 @@
# 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)

View File

@@ -6,8 +6,8 @@ Boost.process is a library for comfortable management of processes, released wit
| Branches | Linux / Windows | Code coverage | Matrix |
|----------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| Develop: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/develop/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-develop-lightgray.svg)](http://www.boost.org/development/tests/develop/developer/process.html) |
| Master: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg?ref=refs/heads/develop)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/master/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-master-lightgray.svg)](http://www.boost.org/development/tests/master/developer/process.html) |
| Develop: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/develop/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-develop-lightgray.svg)](https://regression.boost.io/develop/developer/process.html) |
| Master: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg?ref=refs/heads/develop)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/master/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-master-lightgray.svg)](https://regression.boost.io/master/developer/process.html) |

View File

@@ -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.

View File

@@ -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]
----

View File

@@ -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>

View File

@@ -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>());
};
----

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{.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;
};
----
----

View File

@@ -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

View File

@@ -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[]
}
}

View 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

View File

@@ -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

View File

@@ -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.
*

View File

@@ -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 */

View File

@@ -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());

View File

@@ -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)
@@ -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_);
}
};

View File

@@ -332,6 +332,7 @@ struct basic_process_handle_fd_or_signal
int dummy;
#endif
pid_type pid_;
native_exit_code_type & exit_code;
bool needs_post = true;
template<typename Self>
@@ -344,11 +345,10 @@ struct basic_process_handle_fd_or_signal
template<typename Self>
void operator()(Self &&self, error_code ec, int = 0)
{
native_exit_code_type exit_code{};
int wait_res = -1;
if (pid_ <= 0) // error, complete early
ec = net::error::bad_descriptor;
else
else if (process_is_running(exit_code))
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
@@ -391,18 +391,19 @@ struct basic_process_handle_fd_or_signal
template<typename Self>
void operator()(Self &&self, native_exit_code_type code, error_code ec)
{
self.complete(ec, code);
self.complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
async_wait_op_{descriptor_, signal_set_, pid_}, handler, descriptor_))
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_))
{
return net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
async_wait_op_{descriptor_, signal_set_, pid_}, handler, descriptor_);
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_);
}
};
}

View File

@@ -308,7 +308,8 @@ struct basic_process_handle_signal
net::basic_signal_set<Executor> &handle;
pid_type pid_;
native_exit_code_type & exit_code;
template<typename Self>
void operator()(Self &&self)
{
@@ -319,19 +320,18 @@ struct basic_process_handle_signal
}
template<typename Self>
void operator()(Self &&self, error_code ec, int sig)
void operator()(Self &&self, error_code ec, int /*sig*/)
{
if (ec == net::error::operation_aborted &&
self.get_cancellation_state().cancelled()
== net::cancellation_type::none)
ec.clear();
native_exit_code_type exit_code = -1;
int wait_res = -1;
if (pid_ <= 0) // error, complete early
ec = net::error::bad_descriptor;
else if (!ec)
else if (!ec && process_is_running(exit_code))
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
@@ -345,7 +345,7 @@ struct basic_process_handle_signal
}
const auto exec = self.get_executor();
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
net::dispatch(exec, net::append(std::move(self), ec));
}
#else
signal_set_dummy_ dummy_;
@@ -360,20 +360,21 @@ struct basic_process_handle_signal
}
#endif
template<typename Self>
void operator()(Self &&self, native_exit_code_type code, error_code ec)
void operator()(Self &&self, error_code ec)
{
self.complete(ec, code);
self.complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
async_wait_op_{signal_set_, pid_}, handler, signal_set_))
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_))
{
return net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
async_wait_op_{signal_set_, pid_}, handler, signal_set_);
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_);
}
};

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)
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_
);
}
};

View File

@@ -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");

View File

@@ -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;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -140,7 +140,7 @@ struct pipe_fork_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};
}
ctx.notify_fork(net::execution_context::fork_parent);

View File

@@ -20,10 +20,12 @@
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/any_io_executor.hpp>
#include <boost/asio/dispatch.hpp>
#include <asio/post.hpp>
#include <utility>
#else
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/core/exchange.hpp>
#endif
@@ -300,6 +302,8 @@ struct basic_process
*/
bool running()
{
if (!process_is_running(exit_status_))
return false;
error_code ec;
native_exit_code_type exit_code{};
auto r = process_handle_.running(exit_code, ec);
@@ -314,6 +318,8 @@ struct basic_process
/// Throwing @overload bool running(error_code & ec)
bool running(error_code & ec) noexcept
{
if (!process_is_running(exit_status_))
return false;
native_exit_code_type exit_code{};
auto r = process_handle_.running(exit_code, ec);
if (!ec && !r)
@@ -355,23 +361,22 @@ private:
}
};
net::post(handle.get_executor(),
completer{static_cast<int>(res), std::move(self)});
net::dispatch(
net::get_associated_immediate_executor(handle, handle.get_executor()),
completer{static_cast<int>(res), std::move(self)});
}
else
handle.async_wait(std::move(self));
handle.async_wait(res, std::move(self));
}
template<typename Self>
void operator()(Self && self, error_code ec, native_exit_code_type code)
void operator()(Self && self, error_code ec)
{
if (!ec && process_is_running(code))
handle.async_wait(std::move(self));
if (!ec && process_is_running(res))
handle.async_wait(res, std::move(self));
else
{
if (!ec)
res = code;
std::move(self).complete(ec, evaluate_exit_code(code));
std::move(self).complete(ec, evaluate_exit_code(res));
}
}
};

View File

@@ -130,8 +130,8 @@ struct process_io_binding
template<typename ReadablePipe>
process_io_binding(ReadablePipe & readable_pipe,
typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STDIN_FILENO>::type * = nullptr)
process_io_binding(ReadablePipe & pipe,
typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STD_INPUT_HANDLE>::type * = nullptr)
{
net::detail::native_pipe_handle p[2];
error_code ec;
@@ -145,8 +145,8 @@ struct process_io_binding
template<typename WritablePipe>
process_io_binding(WritablePipe & writable_pipe,
typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STDIN_FILENO>::type * = nullptr)
process_io_binding(WritablePipe & pipe,
typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STD_INPUT_HANDLE>::type * = nullptr)
{
net::detail::native_pipe_handle p[2];
error_code ec;
@@ -184,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;
@@ -267,7 +267,7 @@ struct process_io_binding
return ec;
}
error_code on_exec_setup(posix::default_launcher & launcher,
error_code on_exec_setup(posix::default_launcher &,
const filesystem::path &, const char * const *)
{
if (::dup2(fd, target) == -1)
@@ -308,7 +308,7 @@ typedef process_io_binding<STDERR_FILENO> process_error_binding;
* * @code {.cpp}
* asio::io_context ctx;
* /// C++17
* v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.stderr=nullptr});
* v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.err=nullptr});
* /// C++11 & C++14
* v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{ {}, {}, nullptr});
* stdin ^ ^ stderr

View File

@@ -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

View File

@@ -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

View File

@@ -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{};

View File

@@ -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

View File

@@ -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)
{

View File

@@ -179,7 +179,6 @@ std::vector<pid_type> child_pids(pid_type pid, error_code & ec)
{
std::vector<pid_type> vec;
#if defined(PROC_PPID_ONLY)
vec.resize(proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, nullptr, 0) / sizeof(pid_type));
const auto sz = proc_listpids(PROC_PPID_ONLY, (uint32_t)pid, &vec[0], sizeof(pid_type) * vec.size());
if (sz < 0)
@@ -189,7 +188,14 @@ std::vector<pid_type> child_pids(pid_type pid, error_code & ec)
}
vec.resize(sz);
#else
BOOST_PROCESS_V2_ASSIGN_EC(ec, ENOTSUP, system_category());
std::vector<pid_type> pids = all_pids(ec);
for (std::size_t i = 0; i < pids.size(); i++)
{
if (pid == parent_pid(pids[i], ec))
{
vec.push_back(pids[i]);
}
}
#endif
return vec;
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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
;

View File

@@ -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;
@@ -768,8 +845,93 @@ 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);
}
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -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();