libcarla/include/system/boost/process/v2/stdio.hpp

305 lines
8.9 KiB
C++
Raw Permalink Normal View History

2024-10-18 13:19:59 +08:00
//
// process/stdio.hpp
// ~~~~~~~~
//
// Copyright (c) 2021 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_STDIO_HPP
#define BOOST_PROCESS_V2_STDIO_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/default_launcher.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/connect_pipe.hpp>
#else
#include <boost/asio/connect_pipe.hpp>
#endif
#if defined(BOOST_PROCESS_V2_POSIX)
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
extern "C" intptr_t _get_osfhandle(int fd);
struct handle_closer
{
handle_closer() = default;
handle_closer(bool close) : close(close) {}
handle_closer(DWORD flags) : close(false), flags{flags} {}
void operator()(HANDLE h) const
{
if (close)
::CloseHandle(h);
else if (flags != 0xFFFFFFFFu)
::SetHandleInformation(h, 0xFFFFFFFFu, flags);
}
bool close{false};
DWORD flags{0xFFFFFFFFu};
};
template<DWORD Io>
struct process_io_binding
{
HANDLE prepare()
{
auto hh = h.get();
::SetHandleInformation(hh, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
return hh;
}
std::unique_ptr<void, handle_closer> h{::GetStdHandle(Io), false};
static DWORD get_flags(HANDLE h)
{
DWORD res;
if (!::GetHandleInformation(h, &res))
detail::throw_last_error("get_flags");
return res;
}
process_io_binding() = default;
template<typename Stream>
process_io_binding(Stream && str, decltype(std::declval<Stream>().native_handle()) = nullptr)
: process_io_binding(str.native_handle())
{}
process_io_binding(FILE * f) : process_io_binding(_get_osfhandle(_fileno(f))) {}
process_io_binding(HANDLE h) : h{h, get_flags(h)} {}
process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("NUL")) {}
process_io_binding(const filesystem::path & pth)
: h(::CreateFileW(
pth.c_str(),
Io == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr
), true)
{
}
template<typename Executor>
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & readable_pipe,
typename std::enable_if<Target != STD_INPUT_HANDLE, Executor*>::type = 0)
{
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
if (ec)
return ;
h = std::unique_ptr<void, handle_closer>{p[1], true};
readable_pipe.assign(p[0], ec);
}
template<typename Executor>
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & writable_pipe,
typename std::enable_if<Target == STD_INPUT_HANDLE, Executor*>::type = 0)
{
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
if (ec)
return ;
h = std::unique_ptr<void, handle_closer>{p[0], true};
writable_pipe.assign(p[1], ec);
}
};
typedef process_io_binding<STD_INPUT_HANDLE> process_input_binding;
typedef process_io_binding<STD_OUTPUT_HANDLE> process_output_binding;
typedef process_io_binding<STD_ERROR_HANDLE> process_error_binding;
#else
template<int Target>
struct process_io_binding
{
constexpr static int target = Target;
int fd{target};
bool fd_needs_closing{false};
error_code ec;
~process_io_binding()
{
if (fd_needs_closing)
::close(fd);
}
process_io_binding() = default;
template<typename Stream>
process_io_binding(Stream && str, decltype(std::declval<Stream>().native_handle()) = -1)
: process_io_binding(str.native_handle())
{}
process_io_binding(FILE * f) : process_io_binding(fileno(f)) {}
process_io_binding(int fd) : fd(fd) {}
process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("/dev/null")) {}
process_io_binding(const filesystem::path & pth)
: fd(::open(pth.c_str(),
Target == STDIN_FILENO ? O_RDONLY : (O_WRONLY | O_CREAT),
0660)), fd_needs_closing(true)
{
}
template<typename Executor>
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_readable_pipe<Executor> & readable_pipe,
typename std::enable_if<Target != STDIN_FILENO, Executor*>::type = 0)
{
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
if (ec)
return ;
fd = p[1];
if (::fcntl(p[0], F_SETFD, FD_CLOEXEC) == -1)
{
ec = detail::get_last_error();
return ;
}
fd_needs_closing = true;
readable_pipe.assign(p[0], ec);
}
template<typename Executor>
process_io_binding(BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_writable_pipe<Executor> & writable_pipe,
typename std::enable_if<Target == STDIN_FILENO, Executor*>::type = 0)
{
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::native_pipe_handle p[2];
BOOST_PROCESS_V2_ASIO_NAMESPACE::detail::create_pipe(p, ec);
if (ec)
return ;
fd = p[0];
if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
{
ec = detail::get_last_error();
return ;
}
fd_needs_closing = true;
writable_pipe.assign(p[1], ec);
}
error_code on_setup(posix::default_launcher &,
const filesystem::path &, const char * const *)
{
return ec;
}
error_code on_exec_setup(posix::default_launcher & launcher,
const filesystem::path &, const char * const *)
{
if (::dup2(fd, target) == -1)
return get_last_error();
else
return error_code();
}
};
typedef process_io_binding<STDIN_FILENO> process_input_binding;
typedef process_io_binding<STDOUT_FILENO> process_output_binding;
typedef process_io_binding<STDERR_FILENO> process_error_binding;
#endif
}
/// The initializer for the stdio of a subprocess
/** The subprocess initializer has three members:
*
* - in for stdin
* - out for stdout
* - err for stderr
*
* If the initializer is present all three will be set for the subprocess.
* By default they will inherit the stdio handles from the parent process.
* This means that this will forward stdio to the subprocess:
*
* @code {.cpp}
* asio::io_context ctx;
* v2::process proc(ctx, "/bin/bash", {}, v2::process_stdio{});
* @endcode
*
* No constructors are provided in order to support designated initializers
* in later version of C++.
*
* * @code {.cpp}
* asio::io_context ctx;
* /// C++17
* v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.stderr=nullptr});
* /// C++11 & C++14
* v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{ {}, {}, nullptr});
* stdin ^ ^ stderr
* @endcode
*
* Valid initializers for any stdio are:
*
* - `std::nullptr_t` assigning a null-device
* - `FILE*` any open file, including `stdin`, `stdout` and `stderr`
* - a filesystem::path, which will open a readable or writable depending on the direction of the stream
* - `native_handle` any native file handle (`HANDLE` on windows) or file descriptor (`int` on posix)
* - any io-object with a .native_handle() function that is comptaiblie 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.
*
*
*/
struct process_stdio
{
detail::process_input_binding in;
detail::process_output_binding out;
detail::process_error_binding err;
#if defined(BOOST_PROCESS_V2_WINDOWS)
error_code on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &)
{
launcher.startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
launcher.startup_info.StartupInfo.hStdInput = in.prepare();
launcher.startup_info.StartupInfo.hStdOutput = out.prepare();
launcher.startup_info.StartupInfo.hStdError = err.prepare();
launcher.inherit_handles = true;
return error_code {};
};
#else
error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
{
if (::dup2(in.fd, in.target) == -1)
return error_code(errno, system_category());
if (::dup2(out.fd, out.target) == -1)
return error_code(errno, system_category());
if (::dup2(err.fd, err.target) == -1)
return error_code(errno, system_category());
launcher.fd_whitelist.push_back(STDIN_FILENO);
launcher.fd_whitelist.push_back(STDOUT_FILENO);
launcher.fd_whitelist.push_back(STDERR_FILENO);
return error_code {};
};
#endif
};
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_STDIO_HPP