update boost

This commit is contained in:
2023-11-24 12:56:13 -06:00
parent cfc99971af
commit 19d727037a
9260 changed files with 849256 additions and 299957 deletions

View File

@@ -0,0 +1,113 @@
// 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_BIND_FD_HPP
#define BOOST_PROCESS_V2_POSIX_BIND_FD_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/default_launcher.hpp>
#include <fcntl.h>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace posix
{
/// Utility class to bind a file descriptor to an explicit file descriptor for the child process.
struct bind_fd
{
int target;
int fd;
bool fd_needs_closing{false};
~bind_fd()
{
if (fd_needs_closing)
::close(fd);
}
bind_fd() = delete;
/// Inherit file descriptor with the same value.
/**
* This will pass descriptor 42 as 42 to the child process:
* @code
* process p{"test", {}, posix::bind_fd(42)};
* @endcode
*/
bind_fd(int target) : target(target), fd(target) {}
/// Inherit an asio io-object as a given file descriptor to the child process.
/**
* This will pass the tcp::socket, as 42 to the child process:
* @code
* extern tcp::socket sock;
* process p{"test", {}, posix::bind_fd(42, sock)};
* @endcode
*/
template<typename Stream>
bind_fd(int target, Stream && str, decltype(std::declval<Stream>().native_handle()) = -1)
: bind_fd(target, str.native_handle())
{}
/// Inherit a `FILE` as a given file descriptor to the child process.
/**
* This will pass the given `FILE*`, as 42 to the child process:
* @code
* process p{"test", {}, posix::bind_fd(42, stderr)};
* @endcode
*/
bind_fd(int target, FILE * f) : bind_fd(target, fileno(f)) {}
/// Inherit a file descriptor with as a different value.
/**
* This will pass 24 as 42 to the child process:
* @code
* process p{"test", {}, posix::bind_fd(42, 24)};
* @endcode
*/
bind_fd(int target, int fd) : target(target), fd(fd) {}
/// Inherit a null device as a set descriptor.
/**
* This will pass 24 as 42 to the child process:
* @code
* process p{"test", {}, posix::bind_fd(42, nullptr)};
* @endcode
*/
bind_fd(int target, std::nullptr_t) : bind_fd(target, filesystem::path("/dev/null")) {}
/// Inherit a newly opened-file as a set descriptor.
/**
* This will pass 24 as 42 to the child process:
* @code
* process p{"test", {}, posix::bind_fd(42, "extra-output.txt")};
* @endcode
*/
bind_fd(int target, const filesystem::path & pth, int flags = O_RDWR | O_CREAT)
: target(target), fd(::open(pth.c_str(), flags, 0660)), fd_needs_closing(true)
{
}
error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
{
launcher.fd_whitelist.push_back(target);
return {};
}
/// Implementation of the initialization function.
error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
{
if (::dup2(fd, target) == -1)
return error_code(errno, system_category());
return error_code ();
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_POSIX_BIND_FD_HPP

View File

@@ -0,0 +1,524 @@
// 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_DEFAULT_LAUNCHER
#define BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/cstring_ref.hpp>
#include <boost/process/v2/posix/detail/close_handles.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/detail/utf8.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/execution/executor.hpp>
#include <asio/is_executor.hpp>
#include <asio/execution_context.hpp>
#include <asio/execution/context.hpp>
#include <asio/query.hpp>
#else
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/is_executor.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/execution/context.hpp>
#include <boost/asio/query.hpp>
#endif
#include <fcntl.h>
#include <unistd.h>
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__)
extern "C" { extern char **environ; }
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
template<typename Executor>
struct basic_process;
namespace posix
{
namespace detail
{
struct base {};
struct derived : base {};
template<typename Launcher, typename Init>
inline error_code invoke_on_setup(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init && init, base && )
{
return error_code{};
}
template<typename Launcher, typename Init>
inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init && init, derived && )
-> decltype(init.on_setup(launcher, executable, cmd_line))
{
return init.on_setup(launcher, executable, cmd_line);
}
template<typename Launcher>
inline error_code on_setup(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line))
{
return error_code{};
}
template<typename Launcher, typename Init1, typename ... Inits>
inline error_code on_setup(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init1 && init1, Inits && ... inits)
{
auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{});
if (ec)
return ec;
else
return on_setup(launcher, executable, cmd_line, inits...);
}
template<typename Launcher, typename Init>
inline void invoke_on_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec, Init && init, base && )
{
}
template<typename Launcher, typename Init>
inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec, Init && init, derived && )
-> decltype(init.on_error(launcher, ec, executable, cmd_line, ec))
{
init.on_error(launcher, executable, cmd_line, ec);
}
template<typename Launcher>
inline void on_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec)
{
}
template<typename Launcher, typename Init1, typename ... Inits>
inline void on_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec,
Init1 && init1, Inits && ... inits)
{
invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{});
on_error(launcher, executable, cmd_line, ec, inits...);
}
template<typename Launcher, typename Init>
inline void invoke_on_success(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init && init, base && )
{
}
template<typename Launcher, typename Init>
inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init && init, derived && )
-> decltype(init.on_success(launcher, executable, cmd_line))
{
init.on_success(launcher, executable, cmd_line);
}
template<typename Launcher>
inline void on_success(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line))
{
}
template<typename Launcher, typename Init1, typename ... Inits>
inline void on_success(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init1 && init1, Inits && ... inits)
{
invoke_on_success(launcher, executable, cmd_line, init1, derived{});
on_success(launcher, executable, cmd_line, inits...);
}
template<typename Launcher, typename Init>
inline void invoke_on_fork_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec, Init && init, base && )
{
}
template<typename Launcher, typename Init>
inline auto invoke_on_fork_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec, Init && init, derived && )
-> decltype(init.on_fork_error(launcher, ec, executable, cmd_line, ec))
{
init.on_fork_error(launcher, executable, cmd_line, ec);
}
template<typename Launcher>
inline void on_fork_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec)
{
}
template<typename Launcher, typename Init1, typename ... Inits>
inline void on_fork_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec,
Init1 && init1, Inits && ... inits)
{
invoke_on_fork_error(launcher, executable, cmd_line, ec, init1, derived{});
on_fork_error(launcher, executable, cmd_line, ec, inits...);
}
template<typename Launcher, typename Init>
inline void invoke_on_fork_success(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init && init, base && )
{
}
template<typename Launcher, typename Init>
inline auto invoke_on_fork_success(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init && init, derived && )
-> decltype(init.on_fork_success(launcher, executable, cmd_line))
{
init.on_fork_success(launcher, executable, cmd_line);
}
template<typename Launcher>
inline void on_fork_success(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line))
{
}
template<typename Launcher, typename Init1, typename ... Inits>
inline void on_fork_success(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init1 && init1, Inits && ... inits)
{
invoke_on_fork_success(launcher, executable, cmd_line, init1, derived{});
on_fork_success(launcher, executable, cmd_line, inits...);
}
template<typename Launcher, typename Init>
inline error_code invoke_on_exec_setup(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init && init, base && )
{
return error_code{};
}
template<typename Launcher, typename Init>
inline auto invoke_on_exec_setup(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init && init, derived && )
-> decltype(init.on_exec_setup(launcher, executable, cmd_line))
{
return init.on_exec_setup(launcher, executable, cmd_line);
}
template<typename Launcher>
inline error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line))
{
return error_code{};
}
template<typename Launcher, typename Init1, typename ... Inits>
inline error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
Init1 && init1, Inits && ... inits)
{
auto ec = invoke_on_exec_setup(launcher, executable, cmd_line, init1, derived{});
if (ec)
return ec;
else
return on_exec_setup(launcher, executable, cmd_line, inits...);
}
template<typename Launcher, typename Init>
inline void invoke_on_exec_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec, Init && init, base && )
{
}
template<typename Launcher, typename Init>
inline auto invoke_on_exec_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec, Init && init, derived && )
-> decltype(init.on_exec_error(launcher, ec, executable, cmd_line, ec))
{
init.on_exec_error(launcher, executable, cmd_line, ec);
}
template<typename Launcher>
inline void on_exec_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec)
{
}
template<typename Launcher, typename Init1, typename ... Inits>
inline void on_exec_error(Launcher & launcher, const filesystem::path &executable,
const char * const * (&cmd_line),
const error_code & ec,
Init1 && init1, Inits && ... inits)
{
invoke_on_exec_error(launcher, executable, cmd_line, ec, init1, derived{});
on_exec_error(launcher, executable, cmd_line, ec, inits...);
}
}
/// The default launcher for processes on windows.
struct default_launcher
{
/// The pointer to the environment forwarded to the subprocess.
const char * const * env = ::environ;
/// The pid of the subprocess - will be assigned after fork.
int pid = -1;
/// The whitelist for file descriptors.
std::vector<int> fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
default_launcher() = default;
template<typename ExecutionContext, typename Args, typename ... Inits>
auto operator()(ExecutionContext & context,
const typename std::enable_if<std::is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::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, "default_launcher");
return proc;
}
template<typename ExecutionContext, typename Args, typename ... Inits>
auto operator()(ExecutionContext & context,
error_code & ec,
const typename std::enable_if<std::is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
{
return (*this)(context.get_executor(), 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<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::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, "default_launcher");
return proc;
}
template<typename Executor, typename Args, typename ... Inits>
auto operator()(Executor exec,
error_code & ec,
const typename std::enable_if<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::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));
{
pipe_guard pg;
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};
}
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]);
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare);
pid = ::fork();
if (pid == -1)
{
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::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)
{
::close(pg.p[0]);
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child);
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));
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(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
::close(pg.p[1]);
pg.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};
}
}
basic_process<Executor> proc(exec, pid);
detail::on_success(*this, executable, argv, ec, inits...);
return proc;
}
protected:
void ignore_unused(std::size_t ) {}
void close_all_fds(error_code & ec)
{
std::sort(fd_whitelist.begin(), fd_whitelist.end());
detail::close_all(fd_whitelist, ec);
fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
}
struct pipe_guard
{
int p[2];
pipe_guard() : p{-1,-1} {}
~pipe_guard()
{
if (p[0] != -1)
::close(p[0]);
if (p[1] != -1)
::close(p[1]);
}
};
//if we need to allocate something
std::vector<std::string> argv_buffer_;
std::vector<const char *> argv_;
template<typename Args>
const char * const * build_argv_(const filesystem::path & pt, const Args & args,
typename std::enable_if<
std::is_convertible<
decltype(*std::begin(std::declval<Args>())),
cstring_ref>::value>::type * = nullptr)
{
const auto arg_cnt = std::distance(std::begin(args), std::end(args));
argv_.reserve(arg_cnt + 2);
argv_.push_back(pt.native().data());
for (auto && arg : args)
argv_.push_back(arg.c_str());
argv_.push_back(nullptr);
return argv_.data();
}
const char * const * build_argv_(const filesystem::path &, const char ** argv)
{
return argv;
}
template<typename Args>
const char * const * build_argv_(const filesystem::path & pt, const Args & args,
typename std::enable_if<
!std::is_convertible<
decltype(*std::begin(std::declval<Args>())),
cstring_ref>::value>::type * = nullptr)
{
const auto arg_cnt = std::distance(std::begin(args), std::end(args));
argv_.reserve(arg_cnt + 2);
argv_buffer_.reserve(arg_cnt);
argv_.push_back(pt.native().data());
using char_type = typename decay<decltype((*std::begin(std::declval<Args>()))[0])>::type;
for (basic_string_view<char_type> arg : args)
argv_buffer_.push_back(v2::detail::conv_string<char>(arg.data(), arg.size()));
for (auto && arg : argv_buffer_)
argv_.push_back(arg.c_str());
argv_.push_back(nullptr);
return argv_.data();
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER

View File

@@ -0,0 +1,29 @@
// 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_DETAIL_CLOSE_HANDLES_HPP
#define BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_HPP
#include <boost/process/v2/detail/config.hpp>
#include <vector>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace posix
{
namespace detail
{
// whitelist must be ordered
BOOST_PROCESS_V2_DECL void close_all(const std::vector<int> & whitelist,
error_code & ec);
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_HPP

View File

@@ -0,0 +1,194 @@
//
// boost/process/v2/posix/detail/close_handles.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2022 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
//
// 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_DETAIL_CLOSE_HANDLES_IPP
#define BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/posix/detail/close_handles.hpp>
// linux has close_range since 5.19
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
// https://www.freebsd.org/cgi/man.cgi?query=close_range&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html
// https://man.netbsd.org/closefrom.3
// __FreeBSD__
//
// gives us
//
// int closefrom(int fd);
// int close_range(u_int lowfd, u_int highfd, int flags);
#include <unistd.h>
#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM 1
#elif defined(__sun)
/*https://docs.oracle.com/cd/E36784_01/html/E36874/closefrom-3c.html
int fdwalk(int (*func)(void *, int), void *cd);
*/
#include <stdlib.h>
#define BOOST_PROCESS_V2_HAS_PDFORK 1
#elif defined(__linux__)
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)
// https://man7.org/linux/man-pages/man2/close_range.2.html
#include <linux/close_range.h>
#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1
#else
#include <dirent.h>
#endif
#else
#include <dirent.h>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace posix
{
namespace detail
{
#if defined(BOOST_PROCESS_V2_HAS_PDFORK)
void close_all(const std::vector<int> & whitelist, error_code & ec)
{
fdwalk(+[](void * p, int fd)
{
const auto & wl = *static_cast<const std::vector<int>*>(p);
if (std::find(wl.begin(), wl.end(), fd) == wl.end())
return ::close(fd);
else
return 0;
}, const_cast<void*>(static_cast<const void*>(&whitelist)) );
ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error();
}
#elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE_AND_CLOSEFROM)
// freeBSD impl - whitelist must be ordered
void close_all(const std::vector<int> & whitelist, error_code & ec)
{
//the most common scenario is whitelist = {0,1,2}
if (!whitelist.empty())
{
if (whitelist.front() != 0)
::close_range(0, whitelist.front() - 1, 0);
for (std::size_t idx = 0u;
idx < (whitelist.size() - 1u);
idx++)
{
const auto mine = whitelist[idx];
const auto next = whitelist[idx];
if ((mine + 1) != next && (mine != next))
{
::close_range(mine + 1, next - 1, 0);
}
}
::closefrom(whitelist.back() + 1);
}
else
::closefrom(0);
}
#elif defined(BOOST_PROCESS_V2_HAS_CLOSE_RANGE)
// linux impl - whitelist must be ordered
void close_all(const std::vector<int> & whitelist, error_code & ec)
{
// https://patchwork.kernel.org/project/linux-fsdevel/cover/20200602204219.186620-1-christian.brauner@ubuntu.com/
//the most common scenario is whitelist = {0,1,2}
if (!whitelist.empty())
{
if (whitelist.front() != 0)
::close_range(0, whitelist.front() - 1, CLOSE_RANGE_UNSHARE);
for (std::size_t idx = 0u;
idx < (whitelist.size() - 1u);
idx++)
{
const auto mine = whitelist[idx];
const auto next = whitelist[idx];
if ((mine + 1) != next && (mine != next))
{
::close_range(mine + 1, next - 1, CLOSE_RANGE_UNSHARE);
}
}
::close_range(whitelist.back() + 1, std::numeric_limits<int>::max(), CLOSE_RANGE_UNSHARE);
}
else
::close_range(0, std::numeric_limits<int>::max(), CLOSE_RANGE_UNSHARE);
}
#else
// default one
void close_all(const std::vector<int> & whitelist, error_code & ec)
{
std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
if (dir.get() == nullptr)
{
ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error();
return ;
}
auto dir_fd = ::dirfd(dir.get());
if (dir_fd == -1)
{
ec = BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error();
return ;
}
struct ::dirent * ent_p;
while ((ent_p = ::readdir(dir.get())) != nullptr)
{
if (ent_p->d_name[0] == '.')
continue;
const auto conv = std::atoi(ent_p->d_name);
if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
continue;
if (conv == dir_fd
|| (std::find(whitelist.begin(), whitelist.end(), conv) != whitelist.end()))
continue;
::close(conv);
}
}
#endif
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_IPP

View File

@@ -0,0 +1,135 @@
// 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_FORK_AND_FORGET_LAUNCHER_HPP
#define BOOST_PROCESS_V2_POSIX_FORK_AND_FORGET_LAUNCHER_HPP
#include <boost/process/v2/posix/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace posix
{
/// A posix fork launcher that ignores errors after `fork`.
struct fork_and_forget_launcher : default_launcher
{
fork_and_forget_launcher() = default;
template<typename ExecutionContext, typename Args, typename ... Inits>
auto operator()(ExecutionContext & context,
const typename std::enable_if<is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::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, "fork_and_forget_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&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
{
return (*this)(context.get_executor(), 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<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::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, "fork_and_forget_launcher");
return proc;
}
template<typename Executor, typename Args, typename ... Inits>
auto operator()(Executor exec,
error_code & ec,
const typename std::enable_if<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::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));
{
ec = detail::on_setup(*this, executable, argv, inits ...);
if (ec)
{
detail::on_error(*this, executable, argv, ec, inits...);
return basic_process<Executor>(exec);
}
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare);
pid = ::fork();
if (pid == -1)
{
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::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(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_child);
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));
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(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
if (ec)
{
detail::on_error(*this, executable, argv, ec, inits...);
return basic_process<Executor>{exec};
}
}
basic_process<Executor> proc(exec, pid);
detail::on_success(*this, executable, argv, ec, inits...);
return proc;
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_POSIX_FORK_AND_FORGET_LAUNCHER_HPP

View File

@@ -0,0 +1,171 @@
// 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_PDFORK_LAUNCHER_HPP
#define BOOST_PROCESS_V2_POSIX_PDFORK_LAUNCHER_HPP
#include <boost/process/v2/posix/default_launcher.hpp>
#include <unistd.h>
#include <sys/procdesc.h>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace posix
{
/// A launcher using `pdfork`. Default on FreeBSD
struct pdfork_launcher : default_launcher
{
/// The file descriptor of the subprocess. Set after fork.
int fd;
pdfork_launcher() = default;
template<typename ExecutionContext, typename Args, typename ... Inits>
auto operator()(ExecutionContext & context,
const typename std::enable_if<is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::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, "pdfork_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&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
{
return (*this)(context.get_executor(), 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<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::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, "pdfork_launcher");
return proc;
}
template<typename Executor, typename Args, typename ... Inits>
auto operator()(Executor exec,
error_code & ec,
const typename std::enable_if<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::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));
{
pipe_guard pg;
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};
}
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]);
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare);
pid = ::pdfork(&fd, PD_DAEMON | PD_CLOEXEC);
if (pid == -1)
{
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::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(BOOST_PROCESS_V2_ASIO_NAMESPACE::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(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
::close(pg.p[1]);
pg.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};
}
}
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_PDFORK_LAUNCHER_HPP

View File

@@ -0,0 +1,135 @@
// 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_VFORK_LAUNCHER_HPP
#define BOOST_PROCESS_V2_POSIX_VFORK_LAUNCHER_HPP
#include <boost/process/v2/posix/default_launcher.hpp>
#include <unistd.h>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace posix
{
/// A launcher using vfork instead of fork.
struct vfork_launcher : default_launcher
{
vfork_launcher() = default;
template<typename ExecutionContext, typename Args, typename ... Inits>
auto operator()(ExecutionContext & context,
const typename std::enable_if<std::is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::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, "default_launcher");
return proc;
}
template<typename ExecutionContext, typename Args, typename ... Inits>
auto operator()(ExecutionContext & context,
error_code & ec,
const typename std::enable_if<std::is_convertible<
ExecutionContext&, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context&>::value,
filesystem::path >::type & executable,
Args && args,
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
{
return (*this)(context.get_executor(), 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<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::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, "default_launcher");
return proc;
}
template<typename Executor, typename Args, typename ... Inits>
auto operator()(Executor exec,
error_code & ec,
const typename std::enable_if<
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::is_executor<Executor>::value ||
BOOST_PROCESS_V2_ASIO_NAMESPACE::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));
ec = detail::on_setup(*this, executable, argv, inits ...);
if (ec)
{
detail::on_error(*this, executable, argv, ec, inits...);
return basic_process<Executor>(exec);
}
auto & ctx = BOOST_PROCESS_V2_ASIO_NAMESPACE::query(
exec, BOOST_PROCESS_V2_ASIO_NAMESPACE::execution::context);
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_prepare);
pid = ::vfork();
if (pid == -1)
{
ctx.notify_fork(BOOST_PROCESS_V2_ASIO_NAMESPACE::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)
{
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));
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(BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context::fork_parent);
if (ec)
{
detail::on_error(*this, executable, argv, ec, inits...);
return basic_process<Executor>{exec};
}
basic_process<Executor> proc(exec, pid);
detail::on_success(*this, executable, argv, ec, inits...);
return proc;
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_POSIX_VFORK_LAUNCHER_HPP