mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 16:52:14 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd60cc1464 |
@@ -3,7 +3,7 @@
|
|||||||
# Distributed under the Boost Software License, Version 1.0.
|
# Distributed under the Boost Software License, Version 1.0.
|
||||||
# https://www.boost.org/LICENSE_1_0.txt
|
# https://www.boost.org/LICENSE_1_0.txt
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.8...3.31)
|
cmake_minimum_required(VERSION 3.5...3.16)
|
||||||
|
|
||||||
project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ Boost.process is a library for comfortable management of processes, released wit
|
|||||||
|
|
||||||
| Branches | Linux / Windows | Code coverage | Matrix |
|
| Branches | Linux / Windows | Code coverage | Matrix |
|
||||||
|----------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
|----------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| Develop: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](https://regression.boost.io/develop/developer/process.html) |
|
| 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) | [](https://regression.boost.io/master/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) |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ include::../example/env.cpp[tag=current_env]
|
|||||||
The subprocess environment assignment follows the same constraints:
|
The subprocess environment assignment follows the same constraints:
|
||||||
|
|
||||||
.example/env.cpp:34-42
|
.example/env.cpp:34-42
|
||||||
[source,cpp,indent=0]
|
[source,cpp,ident=0]
|
||||||
----
|
----
|
||||||
include::../example/env.cpp[tag=subprocess_env]
|
include::../example/env.cpp[tag=subprocess_env]
|
||||||
----
|
----
|
||||||
@@ -36,7 +36,7 @@ The current environment can be obtained by calling `environment::current` which
|
|||||||
a forward range of `environment::key_value_pair_view`.
|
a forward range of `environment::key_value_pair_view`.
|
||||||
|
|
||||||
.example/env.cpp:48-54
|
.example/env.cpp:48-54
|
||||||
[source,cpp,indent=0]
|
[source,cpp,ident=0]
|
||||||
----
|
----
|
||||||
include::../example/env.cpp[tag=vector_env]
|
include::../example/env.cpp[tag=vector_env]
|
||||||
----
|
----
|
||||||
@@ -44,7 +44,7 @@ include::../example/env.cpp[tag=vector_env]
|
|||||||
Alternatively you can use a map container for the environment.
|
Alternatively you can use a map container for the environment.
|
||||||
|
|
||||||
.example/env.cpp:61-68
|
.example/env.cpp:61-68
|
||||||
[source,cpp,indent=0]
|
[source,cpp,ident=0]
|
||||||
----
|
----
|
||||||
include::../example/env.cpp[tag=map_env]
|
include::../example/env.cpp[tag=map_env]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ struct custom_initializer
|
|||||||
};
|
};
|
||||||
----
|
----
|
||||||
|
|
||||||
NOTE: All the additional launchers for windows inherit `default_launcher`.
|
NTOE: All the additional launchers for windows inherit `default_launcher`.
|
||||||
|
|
||||||
The call sequence is as follows:
|
The call sequence is as follows:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
== `bind_launcher.hpp`
|
== `bind_launcher.hpp`
|
||||||
|
|
||||||
The `bind_launcher` utilities allow on the fly construction of a launcher with bound initializers.
|
The `bind_launcher` utlitities allow on the fly construction of a launcher with bound initializers.
|
||||||
|
|
||||||
[source,cpp]
|
[source,cpp]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ The `default_launcher` is the standard way of creating a process.
|
|||||||
asio::io_context ctx;
|
asio::io_context ctx;
|
||||||
process proc(ctx.get_executor(), "test", {});
|
process proc(ctx.get_executor(), "test", {});
|
||||||
// equivalent to
|
// equivalent to
|
||||||
process proc = default_launcher()(ctx.get_executor(), "test", {});
|
process prod = default_launcher()(ctx.get_executor(), "test", {});
|
||||||
----
|
----
|
||||||
|
|
||||||
It has four overloads:
|
It has four overloads:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
== `popen.hpp`
|
== `popen.hpp`
|
||||||
[#popen]
|
[#popen]
|
||||||
|
|
||||||
`popen` is a class that launches a process and connect stdin & stdout to pipes.
|
`popen` is a class that launches a process and connect stdin & stderr to pipes.
|
||||||
|
|
||||||
[source,cpp]
|
[source,cpp]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct bind_fd
|
|||||||
process p{"test", {}, posix::bind_fd(42, 24)};
|
process p{"test", {}, posix::bind_fd(42, 24)};
|
||||||
|
|
||||||
*/
|
*/
|
||||||
bind_fd(int target, int fd);
|
bind_fd(int target, int fd):
|
||||||
|
|
||||||
// Inherit a null device as a set descriptor.
|
// Inherit a null device as a set descriptor.
|
||||||
/* This will a null device as 42 to the child process:
|
/* This will a null device as 42 to the child process:
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ struct basic_process
|
|||||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||||
explicit basic_process(
|
explicit basic_process(
|
||||||
ExecutionContext & context,
|
ExecutionContext & context,
|
||||||
const filesystem::path& exe,
|
const filesystem::path&>::type exe,
|
||||||
Args&& args, Inits&&... inits);
|
Args&& args, Inits&&... inits);
|
||||||
|
|
||||||
// Attach to an existing process
|
// Attach to an existing process
|
||||||
@@ -142,7 +142,7 @@ struct basic_process
|
|||||||
native_handle_type native_handle() {return process_handle_.native_handle(); }
|
native_handle_type native_handle() {return process_handle_.native_handle(); }
|
||||||
|
|
||||||
// Return the evaluated exit_code.
|
// Return the evaluated exit_code.
|
||||||
int exit_code() const;
|
int exit_code() cons;
|
||||||
|
|
||||||
// Get the id of the process;
|
// Get the id of the process;
|
||||||
pid_type id() const;
|
pid_type id() const;
|
||||||
|
|||||||
@@ -35,57 +35,12 @@ Valid initializers for any stdio are:
|
|||||||
|
|
||||||
- `std::nullptr_t` assigning a null-device
|
- `std::nullptr_t` assigning a null-device
|
||||||
- `FILE*` any open file, including `stdin`, `stdout` and `stderr`
|
- `FILE*` any open file, including `stdin`, `stdout` and `stderr`
|
||||||
- `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`, or a pipe object.
|
|
||||||
- a filesystem::path, which will open a readable or writable depending on the direction of the stream
|
- 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.
|
- `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.
|
||||||
|
|
||||||
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]
|
[source,cpp]
|
||||||
@@ -97,4 +52,4 @@ struct process_stdio
|
|||||||
__implementation_defined__ out;
|
__implementation_defined__ out;
|
||||||
__implementation_defined__ err;
|
__implementation_defined__ err;
|
||||||
};
|
};
|
||||||
----
|
----
|
||||||
@@ -162,9 +162,7 @@ struct exe_cmd_init : handler_base_ext
|
|||||||
}
|
}
|
||||||
static exe_cmd_init<Char> exe_args_shell(string_type && exe, std::vector<string_type> && args)
|
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::vector<string_type> args_ = {c_arg(Char()), std::move(exe)};
|
||||||
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()));
|
args_.insert(args_.end(), std::make_move_iterator(args.begin()), std::make_move_iterator(args.end()));
|
||||||
string_type sh = get_shell(Char());
|
string_type sh = get_shell(Char());
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,6 @@ struct basic_process_handle_fd
|
|||||||
basic_process_handle_fd(executor_type executor, pid_type pid)
|
basic_process_handle_fd(executor_type executor, pid_type pid)
|
||||||
: pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0))
|
: 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)
|
basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle)
|
||||||
|
|||||||
@@ -44,14 +44,9 @@ std::basic_string<CharOut, Traits, Allocator> conv_string(
|
|||||||
if (ec)
|
if (ec)
|
||||||
detail::throw_error(ec, "size_as_utf8");
|
detail::throw_error(ec, "size_as_utf8");
|
||||||
|
|
||||||
|
|
||||||
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
||||||
res.resize(req_size);
|
res.resize(req_size);
|
||||||
|
|
||||||
if (req_size == 0)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
|
|
||||||
auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec);
|
auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec);
|
||||||
if (ec)
|
if (ec)
|
||||||
detail::throw_error(ec, "convert_to_utf8");
|
detail::throw_error(ec, "convert_to_utf8");
|
||||||
@@ -75,9 +70,6 @@ std::basic_string<CharOut, Traits, Allocator> conv_string(
|
|||||||
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
||||||
res.resize(req_size);
|
res.resize(req_size);
|
||||||
|
|
||||||
if (req_size == 0)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec);
|
auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec);
|
||||||
if (ec)
|
if (ec)
|
||||||
detail::throw_error(ec, "convert_to_wide");
|
detail::throw_error(ec, "convert_to_wide");
|
||||||
|
|||||||
@@ -1762,12 +1762,9 @@ struct process_environment
|
|||||||
std::vector<environment::key_value_pair> env_buffer;
|
std::vector<environment::key_value_pair> env_buffer;
|
||||||
std::vector<wchar_t> unicode_env;
|
std::vector<wchar_t> unicode_env;
|
||||||
|
|
||||||
|
BOOST_PROCESS_V2_DECL
|
||||||
error_code on_setup(windows::default_launcher & launcher,
|
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
|
#else
|
||||||
|
|
||||||
@@ -1816,13 +1813,7 @@ struct process_environment
|
|||||||
|
|
||||||
BOOST_PROCESS_V2_DECL
|
BOOST_PROCESS_V2_DECL
|
||||||
error_code on_setup(posix::default_launcher & launcher,
|
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<environment::key_value_pair> env_buffer;
|
||||||
std::vector<const char *> env;
|
std::vector<const char *> env;
|
||||||
|
|||||||
@@ -352,9 +352,9 @@ struct default_launcher
|
|||||||
}
|
}
|
||||||
fd_whitelist.push_back(pg.p[1]);
|
fd_whitelist.push_back(pg.p[1]);
|
||||||
|
|
||||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
|
||||||
auto & ctx = net::query(
|
auto & ctx = net::query(
|
||||||
exec, net::execution::context);
|
exec, net::execution::context);
|
||||||
|
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||||
#endif
|
#endif
|
||||||
pid = ::fork();
|
pid = ::fork();
|
||||||
@@ -386,7 +386,7 @@ struct default_launcher
|
|||||||
ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||||
::_exit(EXIT_FAILURE);
|
::exit(EXIT_FAILURE);
|
||||||
return basic_process<Executor>{exec};
|
return basic_process<Executor>{exec};
|
||||||
}
|
}
|
||||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
#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());
|
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||||
::_exit(EXIT_FAILURE);
|
::exit(EXIT_FAILURE);
|
||||||
return basic_process<Executor>{exec};
|
return basic_process<Executor>{exec};
|
||||||
}
|
}
|
||||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
#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)));
|
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||||
::_exit(EXIT_FAILURE);
|
::exit(EXIT_FAILURE);
|
||||||
return basic_process<Executor>{exec};
|
return basic_process<Executor>{exec};
|
||||||
}
|
}
|
||||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ struct pipe_fork_launcher : default_launcher
|
|||||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||||
::_exit(EXIT_FAILURE);
|
::exit(EXIT_FAILURE);
|
||||||
return basic_process<Executor>{exec};
|
return basic_process<Executor>{exec};
|
||||||
}
|
}
|
||||||
ctx.notify_fork(net::execution_context::fork_parent);
|
ctx.notify_fork(net::execution_context::fork_parent);
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ struct process_io_binding
|
|||||||
process_io_binding & operator=(const process_io_binding &) = delete;
|
process_io_binding & operator=(const process_io_binding &) = delete;
|
||||||
|
|
||||||
process_io_binding(process_io_binding && other) noexcept
|
process_io_binding(process_io_binding && other) noexcept
|
||||||
: fd(other.fd), fd_needs_closing(other.fd_needs_closing), ec(other.ec)
|
: fd(other.fd), fd_needs_closing(other.fd), ec(other.ec)
|
||||||
{
|
{
|
||||||
other.fd = target;
|
other.fd = target;
|
||||||
other.fd_needs_closing = false;
|
other.fd_needs_closing = false;
|
||||||
|
|||||||
@@ -225,9 +225,6 @@ struct default_launcher
|
|||||||
INVALID_HANDLE_VALUE,
|
INVALID_HANDLE_VALUE,
|
||||||
INVALID_HANDLE_VALUE},
|
INVALID_HANDLE_VALUE},
|
||||||
nullptr};
|
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
|
/// The process_information that gets assigned after a call to CreateProcess
|
||||||
PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
|
PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
|
||||||
|
|
||||||
@@ -296,12 +293,6 @@ struct default_launcher
|
|||||||
Args && args,
|
Args && args,
|
||||||
Inits && ... inits ) -> enable_init<Executor, Inits...>
|
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));
|
auto command_line = this->build_command_line(executable, std::forward<Args>(args));
|
||||||
|
|
||||||
ec = detail::on_setup(*this, executable, command_line, inits...);
|
ec = detail::on_setup(*this, executable, command_line, inits...);
|
||||||
@@ -361,6 +352,7 @@ struct default_launcher
|
|||||||
BOOST_PROCESS_V2_DECL static
|
BOOST_PROCESS_V2_DECL static
|
||||||
std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
|
std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
|
||||||
basic_string_view<wchar_t> ws);
|
basic_string_view<wchar_t> ws);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -403,7 +395,6 @@ struct default_launcher
|
|||||||
{
|
{
|
||||||
return detail::conv_string<wchar_t>(arg.data(), arg.size());
|
return detail::conv_string<wchar_t>(arg.data(), arg.size());
|
||||||
});
|
});
|
||||||
|
|
||||||
return build_command_line_impl(pt, argw, L"");
|
return build_command_line_impl(pt, argw, L"");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,11 +406,10 @@ struct default_launcher
|
|||||||
{
|
{
|
||||||
std::wstring buffer;
|
std::wstring buffer;
|
||||||
buffer.resize(escaped_argv_length(pt.native()));
|
buffer.resize(escaped_argv_length(pt.native()));
|
||||||
|
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
|
||||||
if (!buffer.empty())
|
|
||||||
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return build_command_line_impl(pt, args, *std::begin(args));
|
return build_command_line_impl(pt, args, *std::begin(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,4 +438,4 @@ BOOST_PROCESS_V2_END_NAMESPACE
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|
||||||
@@ -17,7 +17,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||||
|
|
||||||
error_code process_environment::do_setup(windows::default_launcher & launcher)
|
error_code process_environment::on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &)
|
||||||
{
|
{
|
||||||
if (!unicode_env.empty() && !ec)
|
if (!unicode_env.empty() && !ec)
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@ error_code process_environment::do_setup(windows::default_launcher & launcher)
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
error_code process_environment::do_setup(posix::default_launcher & launcher)
|
error_code process_environment::on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||||
{
|
{
|
||||||
launcher.env = env.data();
|
launcher.env = env.data();
|
||||||
return error_code{};
|
return error_code{};
|
||||||
|
|||||||
@@ -24,72 +24,54 @@ namespace windows
|
|||||||
if (ws.empty())
|
if (ws.empty())
|
||||||
return 2u; // just quotes
|
return 2u; // just quotes
|
||||||
|
|
||||||
|
constexpr static auto space = L' ';
|
||||||
constexpr static auto quote = L'"';
|
constexpr static auto quote = L'"';
|
||||||
const auto needs_quotes = ws.find_first_of(L" \t") != basic_string_view<wchar_t>::npos;
|
|
||||||
|
|
||||||
std::size_t needed_escapes = 0u;
|
const auto has_space = ws.find(space) != basic_string_view<wchar_t>::npos;
|
||||||
for (auto itr = ws.begin(); itr != ws.end(); itr ++)
|
const auto quoted = (ws.front() == quote) && (ws.back() == quote);
|
||||||
{
|
const auto needs_escape = has_space && !quoted ;
|
||||||
if (*itr == quote)
|
|
||||||
needed_escapes++;
|
if (!needs_escape)
|
||||||
else if (*itr == L'\\')
|
return ws.size();
|
||||||
{
|
else
|
||||||
auto nx = std::next(itr);
|
return ws.size() + std::count(ws.begin(), ws.end(), quote) + 2u;
|
||||||
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,
|
std::size_t default_launcher::escape_argv_string(wchar_t * itr, std::size_t max_size,
|
||||||
basic_string_view<wchar_t> ws)
|
basic_string_view<wchar_t> ws)
|
||||||
{
|
{
|
||||||
constexpr static auto quote = L'"';
|
const auto sz = escaped_argv_length(ws);
|
||||||
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)
|
if (sz > max_size)
|
||||||
return 0u;
|
return 0u;
|
||||||
|
|
||||||
if (ws.empty())
|
if (ws.empty())
|
||||||
{
|
{
|
||||||
itr[0] = quote;
|
itr[0] = L'"';
|
||||||
itr[1] = quote;
|
itr[1] = L'"';
|
||||||
return 2u;
|
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 end = itr + sz;
|
||||||
const auto begin = itr;
|
const auto begin = itr;
|
||||||
if (needs_quotes)
|
*(itr ++) = L'"';
|
||||||
*(itr++) = quote;
|
for (auto wc : ws)
|
||||||
|
|
||||||
for (auto it = ws.begin(); it != ws.end(); it ++)
|
|
||||||
{
|
{
|
||||||
if (*it == quote) // makes it \"
|
if (wc == L'"')
|
||||||
*(itr++) = L'\\';
|
*(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needs_quotes)
|
*(itr ++) = L'"';
|
||||||
*(itr++) = quote;
|
|
||||||
return itr - begin;
|
return itr - begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,18 +108,13 @@ namespace windows
|
|||||||
auto tl = get_thread_attribute_list(ec);
|
auto tl = get_thread_attribute_list(ec);
|
||||||
if (ec)
|
if (ec)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto itr = std::unique(inherited_handles.begin(), inherited_handles.end());
|
|
||||||
auto size = std::distance(inherited_handles.begin(), itr);
|
|
||||||
|
|
||||||
if (!::UpdateProcThreadAttribute(
|
if (!::UpdateProcThreadAttribute(
|
||||||
tl, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
tl, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
||||||
inherited_handles.data(), size * sizeof(HANDLE), nullptr, nullptr))
|
inherited_handles.data(), inherited_handles.size() * sizeof(HANDLE), nullptr, nullptr))
|
||||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
BOOST_PROCESS_V2_END_NAMESPACE
|
BOOST_PROCESS_V2_END_NAMESPACE
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
enable_testing()
|
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)
|
function(boost_process_v2_standalone_test name)
|
||||||
add_executable(boost_process_v2_${name} ${name}.cpp test_impl.cpp)
|
add_executable(boost_process_v2_${name} ${name}.cpp)
|
||||||
target_link_libraries(boost_process_v2_${name} PUBLIC Boost::process Boost::system Boost::filesystem Boost::unit_test_framework )
|
target_link_libraries(boost_process_v2_${name} 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_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}> )
|
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}> )
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
@@ -21,16 +27,13 @@ target_link_libraries(boost_process_v2_test_target PUBLIC Boost::process Boost::
|
|||||||
|
|
||||||
function(boost_process_v2_test_with_target name)
|
function(boost_process_v2_test_with_target name)
|
||||||
add_executable(boost_process_v2_${name} ${name}.cpp)
|
add_executable(boost_process_v2_${name} ${name}.cpp)
|
||||||
target_link_libraries(boost_process_v2_${name} PUBLIC Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||||
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_dependencies(boost_process_v2_${name} boost_process_v2_test_target)
|
||||||
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}>
|
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}>
|
||||||
-- $<TARGET_FILE:boost_process_v2_test_target>)
|
-- $<TARGET_FILE:boost_process_v2_test_target>)
|
||||||
|
|
||||||
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
boost_process_v2_test_with_target(process)
|
boost_process_v2_test_with_target(process)
|
||||||
boost_process_v2_test_with_target(ext)
|
boost_process_v2_test_with_target(ext)
|
||||||
@@ -33,6 +33,7 @@ project : requirements
|
|||||||
<os>NT,<toolset>cw:<library>ws2_32
|
<os>NT,<toolset>cw:<library>ws2_32
|
||||||
<os>NT,<toolset>gcc:<library>ws2_32
|
<os>NT,<toolset>gcc:<library>ws2_32
|
||||||
<os>NT,<toolset>gcc:<library>Bcrypt
|
<os>NT,<toolset>gcc:<library>Bcrypt
|
||||||
|
<define>BOOST_PROCESS_V2_SEPARATE_COMPILATION=1
|
||||||
<library>/boost/test//included
|
<library>/boost/test//included
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -256,11 +256,8 @@ BOOST_AUTO_TEST_CASE(print_args_spec_out)
|
|||||||
asio::writable_pipe wp{ctx};
|
asio::writable_pipe wp{ctx};
|
||||||
asio::connect_pipe(rp, wp);
|
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});
|
||||||
bpv::process proc(ctx, pth, {"print-args", "&foo", "&", "", "\"\"", "\\\"", "|bar", "\"", "#foobar"},
|
|
||||||
bpv::process_stdio{/*in*/{},/*out*/wp, /*err*/ nullptr});
|
|
||||||
BOOST_CHECK(proc.running());
|
|
||||||
|
|
||||||
wp.close();
|
wp.close();
|
||||||
asio::streambuf st;
|
asio::streambuf st;
|
||||||
@@ -291,18 +288,6 @@ BOOST_AUTO_TEST_CASE(print_args_spec_out)
|
|||||||
trim_end(line);
|
trim_end(line);
|
||||||
BOOST_CHECK_EQUAL("&", 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));
|
BOOST_CHECK(std::getline(is, line));
|
||||||
trim_end(line);
|
trim_end(line);
|
||||||
BOOST_CHECK_EQUAL("|bar", line);
|
BOOST_CHECK_EQUAL("|bar", line);
|
||||||
@@ -311,7 +296,6 @@ BOOST_AUTO_TEST_CASE(print_args_spec_out)
|
|||||||
trim_end(line);
|
trim_end(line);
|
||||||
BOOST_CHECK_EQUAL("\"", line);
|
BOOST_CHECK_EQUAL("\"", line);
|
||||||
|
|
||||||
|
|
||||||
BOOST_CHECK(std::getline(is, line));
|
BOOST_CHECK(std::getline(is, line));
|
||||||
trim_end(line);
|
trim_end(line);
|
||||||
BOOST_CHECK_EQUAL("#foobar", line);
|
BOOST_CHECK_EQUAL("#foobar", line);
|
||||||
@@ -868,70 +852,5 @@ BOOST_AUTO_TEST_CASE(async_terminate_code)
|
|||||||
#endif
|
#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();
|
BOOST_AUTO_TEST_SUITE_END();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user