整理
This commit is contained in:
113
include/boost/process/v2/posix/bind_fd.hpp
Normal file
113
include/boost/process/v2/posix/bind_fd.hpp
Normal 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
|
||||
503
include/boost/process/v2/posix/default_launcher.hpp
Normal file
503
include/boost/process/v2/posix/default_launcher.hpp
Normal file
@@ -0,0 +1,503 @@
|
||||
// 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 <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <crt_externs.h>
|
||||
# if !defined(environ)
|
||||
# define environ (*_NSGetEnviron())
|
||||
# endif
|
||||
#elif defined(__MACH__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun)
|
||||
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, 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, 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 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, 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&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "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&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
{
|
||||
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]);
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
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]);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
#endif
|
||||
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};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
::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...);
|
||||
do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
|
||||
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
|
||||
29
include/boost/process/v2/posix/detail/close_handles.hpp
Normal file
29
include/boost/process/v2/posix/detail/close_handles.hpp
Normal 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
|
||||
144
include/boost/process/v2/posix/fork_and_forget_launcher.hpp
Normal file
144
include/boost/process/v2/posix/fork_and_forget_launcher.hpp
Normal file
@@ -0,0 +1,144 @@
|
||||
// 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&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "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&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "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<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
{
|
||||
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 = 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();
|
||||
if (pid == -1)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
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)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
#endif
|
||||
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};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
|
||||
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
|
||||
180
include/boost/process/v2/posix/pdfork_launcher.hpp
Normal file
180
include/boost/process/v2/posix/pdfork_launcher.hpp
Normal file
@@ -0,0 +1,180 @@
|
||||
// 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&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "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&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pdfork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
{
|
||||
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 = net::query(
|
||||
exec, net::execution::context);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::pdfork(&fd, PD_DAEMON | PD_CLOEXEC);
|
||||
if (pid == -1)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
#endif
|
||||
::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};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
::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...);
|
||||
do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
|
||||
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
|
||||
185
include/boost/process/v2/posix/pipe_fork_launcher.hpp
Normal file
185
include/boost/process/v2/posix/pipe_fork_launcher.hpp
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
/// A launcher using `pipe_fork`. Default on FreeBSD
|
||||
struct pipe_fork_launcher : default_launcher
|
||||
{
|
||||
/// The file descriptor of the subprocess. Set after fork.
|
||||
pipe_fork_launcher() = default;
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pipe_fork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pipe_fork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
int fd = -1;
|
||||
{
|
||||
pipe_guard pg, pg_wait;
|
||||
if (::pipe(pg.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::pipe(pg_wait.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg_wait.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ec = detail::on_setup(*this, executable, argv, inits ...);
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
fd_whitelist.push_back(pg_wait.p[1]);
|
||||
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
::close(pg.p[0]);
|
||||
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
{
|
||||
close_all_fds(ec);
|
||||
}
|
||||
if (!ec)
|
||||
::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
|
||||
|
||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
::close(pg.p[1]);
|
||||
pg.p[1] = -1;
|
||||
::close(pg_wait.p[1]);
|
||||
pg_wait.p[1] = -1;
|
||||
int child_error{0};
|
||||
int count = -1;
|
||||
while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if ((err != EAGAIN) && (err != EINTR))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category());
|
||||
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
std::swap(fd, pg_wait.p[0]);
|
||||
}
|
||||
|
||||
basic_process<Executor> proc(exec, pid, fd);
|
||||
detail::on_success(*this, executable, argv, ec, inits...);
|
||||
return proc;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
140
include/boost/process/v2/posix/vfork_launcher.hpp
Normal file
140
include/boost/process/v2/posix/vfork_launcher.hpp
Normal file
@@ -0,0 +1,140 @@
|
||||
// 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&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "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&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
|
||||
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 = net::query(exec, net::execution::context);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::vfork();
|
||||
if (pid == -1)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
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};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
|
||||
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
|
||||
Reference in New Issue
Block a user