This commit is contained in:
2026-03-23 20:54:41 +08:00
commit e13b3650e9
4596 changed files with 1015768 additions and 0 deletions

View File

@@ -0,0 +1,240 @@
//
// boost/process/v2/bind_launcher.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_BIND_LAUNCHER_HPP
#define BOOST_PROCESS_V2_BIND_LAUNCHER_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
template<std::size_t ... Idx>
struct index_sequence { };
template<std::size_t Size, typename T>
struct make_index_sequence_impl;
template<std::size_t Size, std::size_t ... Idx>
struct make_index_sequence_impl<Size, index_sequence<Idx...>>
{
constexpr make_index_sequence_impl() {}
using type = typename make_index_sequence_impl<Size - 1u, index_sequence<Size - 1u, Idx...>>::type;
};
template<std::size_t ... Idx>
struct make_index_sequence_impl<0u, index_sequence<Idx...>>
{
constexpr make_index_sequence_impl() {}
using type = index_sequence<Idx...>;
};
template<std::size_t Cnt>
struct make_index_sequence
{
using type = typename make_index_sequence_impl<Cnt, index_sequence<>>::type;
};
template<std::size_t Cnt>
using make_index_sequence_t = typename make_index_sequence<Cnt>::type;
}
/** @brief Utility class to bind initializers to a launcher
* @tparam Launcher The inner launcher to be used
* @tparam ...Init The initializers to be prepended.
*
* This can be used when multiple processes shared some settings,
* e.g.
*
*/
template<typename Launcher, typename ... Init>
struct bound_launcher
{
template<typename Launcher_, typename ... Init_>
bound_launcher(Launcher_ && l, Init_ && ... init) :
launcher_(std::forward<Launcher_>(l)), init_(std::forward<Init_>(init)...)
{
}
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>
{
return invoke(detail::make_index_sequence_t<sizeof...(Init)>{},
context,
executable,
std::forward<Args>(args),
std::forward<Inits>(inits)...);
}
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 invoke(detail::make_index_sequence_t<sizeof...(Init)>{},
context, 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>
{
return invoke(detail::make_index_sequence_t<sizeof...(Init)>{},
std::move(exec),
executable,
std::forward<Args>(args),
std::forward<Inits>(inits)...);
}
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>
{
return invoke(detail::make_index_sequence_t<sizeof...(Init)>{},
std::move(exec), ec,
executable,
std::forward<Args>(args),
std::forward<Inits>(inits)...);
}
private:
template<std::size_t ... Idx, typename ExecutionContext, typename Args, typename ... Inits>
auto invoke(detail::index_sequence<Idx...>,
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>
{
return launcher_(context,
executable,
std::forward<Args>(args),
std::get<Idx>(init_)...,
std::forward<Inits>(inits)...);
}
template<std::size_t ... Idx, typename ExecutionContext, typename Args, typename ... Inits>
auto invoke(detail::index_sequence<Idx...>,
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 launcher_(context, ec,
executable,
std::forward<Args>(args),
std::get<Idx>(init_)...,
std::forward<Inits>(inits)...);
}
template<std::size_t ... Idx, typename Executor, typename Args, typename ... Inits>
auto invoke(detail::index_sequence<Idx...>,
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>
{
return launcher_(std::move(exec),
executable,
std::forward<Args>(args),
std::get<Idx>(init_)...,
std::forward<Inits>(inits)...);
}
template<std::size_t ... Idx, typename Executor, typename Args, typename ... Inits>
auto invoke(detail::index_sequence<Idx...>,
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>
{
return launcher_(std::move(exec), ec,
executable,
std::forward<Args>(args),
std::get<Idx>(init_)...,
std::forward<Inits>(inits)...);
}
Launcher launcher_;
std::tuple<Init...> init_;
};
template<typename Launcher, typename ... Init>
auto bind_launcher(Launcher && launcher, Init && ... init)
-> bound_launcher<typename std::decay<Launcher>::type,
typename std::decay<Init>::type...>
{
return bound_launcher<typename std::decay<Launcher>::type,
typename std::decay<Init>::type...>(
std::forward<Launcher>(launcher),
std::forward<Init>(init)...);
}
/// @brief @overload bind_launcher(Launcher && launcher, Init && init)
/// @tparam ...Init The initializer types to bind to the default_launcher.
/// @param ...init The initializers types to bind to the default_launcher.
/// @return The new default_launcher.
template<typename ... Init>
auto bind_default_launcher(Init && ... init)
-> bound_launcher<default_process_launcher,
typename std::decay<Init>::type...>
{
return bound_launcher<default_process_launcher,
typename std::decay<Init>::type...>(
default_process_launcher(),
std::forward<Init>(init)...);
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_BIND_LAUNCHER_HPP

View File

@@ -0,0 +1,232 @@
// 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_CSTRING_REF_HPP
#define BOOST_PROCESS_V2_CSTRING_REF_HPP
#include <boost/process/v2/detail/config.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <string_view>
#else
#include <boost/utility/string_view.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_CONSTEXPR static const char* null_char_(char) {return "";}
BOOST_CONSTEXPR static const wchar_t* null_char_(wchar_t) {return L"";}
BOOST_CONSTEXPR static const char16_t* null_char_(char16_t) {return u"";}
BOOST_CONSTEXPR static const char32_t* null_char_(char32_t) {return U"";}
#if defined(BOOST_PROCESS_V2_HAS_CHAR8_T)
BOOST_CONSTEXPR static const char8_t* null_char_(char8_t) {return u8"";}
#endif
}
#if defined(BOOST_PROCESS_V2_STANDALONE)
using std::basic_string_view;
using std:: string_view;
using std:: wstring_view;
#else
using boost::basic_string_view;
using boost:: string_view;
using boost:: wstring_view;
#endif
/// Small wrapper for a null-terminated string that can be directly passed to C APIS
/** This ref can only be modified by moving the front pointer. It does not store the
* size, but can detect values that can directly be passed to system APIs.
*
* It can be constructed from a `char*` pointer or any class that has a `c_str()`
* member function, e.g. std::string or boost::static_string.
*
*/
template<typename CharT, typename Traits = std::char_traits<CharT>>
struct basic_cstring_ref
{
using value_type = CharT;
using traits_type = Traits;
BOOST_CONSTEXPR basic_cstring_ref() noexcept : view_(detail::null_char_(value_type{})) {}
BOOST_CONSTEXPR basic_cstring_ref(std::nullptr_t) = delete;
BOOST_CONSTEXPR basic_cstring_ref( const value_type* s ) : view_(s) {}
template<typename Source,
typename =
typename std::enable_if<
std::is_same<const value_type,
typename std::remove_pointer<decltype(std::declval<Source>().c_str())>::type
>::value>::type>
BOOST_CONSTEXPR basic_cstring_ref(Source && src) : view_(src.c_str()) {}
BOOST_CONSTEXPR typename basic_string_view<value_type, Traits>::const_pointer c_str() const BOOST_NOEXCEPT
{
return this->data();
}
using string_view_type = basic_string_view<value_type, Traits>;
constexpr operator string_view_type() const {return view_;}
using pointer = CharT *;
using const_pointer = const CharT *;
using reference = CharT &;
using const_reference = const CharT &;
using const_iterator = const_pointer;
using iterator = const_iterator;
using const_reverse_iterator = typename std::reverse_iterator<const_iterator>;
using reverse_iterator = typename std::reverse_iterator<iterator>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
static BOOST_CONSTEXPR size_type npos = -1;
BOOST_CONSTEXPR const_iterator begin() const BOOST_NOEXCEPT {return view_;};
BOOST_CONSTEXPR const_iterator end() const BOOST_NOEXCEPT {return view_ + length();};
BOOST_CONSTEXPR const_iterator cbegin() const BOOST_NOEXCEPT {return view_;};
BOOST_CONSTEXPR const_iterator cend() const BOOST_NOEXCEPT {return view_ + length();};
BOOST_CONSTEXPR const_reverse_iterator rbegin() const BOOST_NOEXCEPT {return reverse_iterator(view_ + length());};
BOOST_CONSTEXPR const_reverse_iterator rend() const BOOST_NOEXCEPT {return reverse_iterator(view_);};
BOOST_CONSTEXPR const_reverse_iterator crbegin() const BOOST_NOEXCEPT {return reverse_iterator(view_ + length());};
BOOST_CONSTEXPR const_reverse_iterator crend() const BOOST_NOEXCEPT {return reverse_iterator(view_);};
BOOST_CONSTEXPR size_type size() const BOOST_NOEXCEPT {return length(); }
BOOST_CONSTEXPR size_type length() const BOOST_NOEXCEPT {return traits_type::length(view_); }
BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT {return (std::numeric_limits<std::size_t>::max)() / sizeof(CharT); }
BOOST_ATTRIBUTE_NODISCARD BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT {return *view_ == *detail::null_char_(CharT{}); }
BOOST_CONSTEXPR const_reference operator[](size_type pos) const {return view_[pos] ;}
BOOST_CXX14_CONSTEXPR const_reference at(size_type pos) const
{
if (pos >= size())
throw_exception(std::out_of_range("cstring-view out of range"));
return view_[pos];
}
BOOST_CONSTEXPR const_reference front() const {return *view_;}
BOOST_CONSTEXPR const_reference back() const {return view_[length() - 1];}
BOOST_CONSTEXPR const_pointer data() const BOOST_NOEXCEPT {return view_;}
BOOST_CXX14_CONSTEXPR void remove_prefix(size_type n) {view_ = view_ + n;}
void swap(basic_cstring_ref& s) BOOST_NOEXCEPT {std::swap(view_, s.view_);}
size_type copy(value_type* s, size_type n, size_type pos = 0) const
{
return traits_type::copy(s, view_ + pos, n) - view_;
}
BOOST_CONSTEXPR basic_cstring_ref substr(size_type pos = 0) const
{
return basic_cstring_ref(view_ + pos);
}
BOOST_CONSTEXPR string_view_type substr(size_type pos, size_type length) const
{
return string_view_type(view_).substr(pos, length);
}
BOOST_CXX14_CONSTEXPR int compare(basic_cstring_ref x) const BOOST_NOEXCEPT
{
auto idx = 0u;
for (; view_[idx] != null_char_()[0] && x[idx] != null_char_()[0]; idx++)
if (!traits_type::eq(view_[idx], x[idx]))
return traits_type::lt(view_[idx], x[idx]) ? -1 : 1;
return traits_type::to_int_type(view_[idx]) -
traits_type::to_int_type(x[idx]); // will compare to null char of either.
}
BOOST_CXX14_CONSTEXPR bool starts_with(string_view_type x) const BOOST_NOEXCEPT
{
if (x.empty())
return true;
auto idx = 0u;
for (; view_[idx] != null_char_()[0] && idx < x.size(); idx++)
if (!traits_type::eq(view_[idx], x[idx]))
return false;
return idx == x.size() || view_[idx] != null_char_()[0];
}
BOOST_CONSTEXPR bool starts_with(value_type x) const BOOST_NOEXCEPT
{
return traits_type::eq(view_[0], x);
}
BOOST_CXX14_CONSTEXPR size_type find( CharT ch, size_type pos = 0 ) const BOOST_NOEXCEPT
{
for (auto p = view_ + pos; *p != *null_char_(); p++)
if (traits_type::eq(*p, ch))
return p - view_;
return npos;
}
friend BOOST_CXX14_CONSTEXPR bool operator==(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT
{
std::size_t idx = 0u;
for (idx = 0u; x[idx] != null_char_()[0] && y[idx] != null_char_()[0]; idx++)
if (!traits_type::eq(x[idx], y[idx]))
return false;
return x[idx] == y[idx];
}
friend BOOST_CXX14_CONSTEXPR bool operator!=(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT
{
std::size_t idx = 0u;
for (idx = 0u; x[idx] != null_char_()[0] &&
y[idx] != null_char_()[0]; idx++)
if (!traits_type::eq(x[idx], y[idx]))
return true;
return x[idx] != y[idx];
}
friend BOOST_CXX14_CONSTEXPR bool operator< (basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) < 0;}
friend BOOST_CXX14_CONSTEXPR bool operator> (basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) > 0;}
friend BOOST_CXX14_CONSTEXPR bool operator<=(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) <= 0;}
friend BOOST_CXX14_CONSTEXPR bool operator>=(basic_cstring_ref x, basic_cstring_ref y) BOOST_NOEXCEPT {return x.compare(y) >= 0;}
// modifiers
void clear() BOOST_NOEXCEPT { view_ = null_char_(); } // Boost extension
std::basic_string<value_type, traits_type> to_string() const {
return std::basic_string<CharT, Traits>(begin(), end());
}
template<typename Allocator>
std::basic_string<value_type, traits_type, Allocator> to_string(const Allocator& a) const {
return std::basic_string<value_type, traits_type, Allocator>(begin(), end(), a);
}
private:
BOOST_CONSTEXPR static const_pointer null_char_() {return detail::null_char_(CharT{});}
const_pointer view_;
};
template<class charT, class traits>
inline std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
const basic_cstring_ref<charT,traits>& str)
{
return os << static_cast<basic_string_view<charT, traits>>(str);
}
template <class charT, class traits>
std::size_t hash_value(basic_string_view<charT, traits> s) {
return boost::hash_range(s.begin(), s.end());
}
using cstring_ref = basic_cstring_ref<char>;
using wcstring_ref = basic_cstring_ref<wchar_t>;
using u16cstring_ref = basic_cstring_ref<char16_t>;
using u32cstring_ref = basic_cstring_ref<char32_t>;
#if defined(BOOST_PROCESS_V2_HAS_CHAR8_T)
using u8cstring_ref = basic_cstring_ref<char8_t>;
#endif
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_CSTRING_REF_HPP

View File

@@ -0,0 +1,67 @@
//
// boost/process/v2/default_launcher.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_DEFAULT_LAUNCHER_HPP
#define BOOST_PROCESS_V2_DEFAULT_LAUNCHER_HPP
#include <boost/process/v2/detail/config.hpp>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <boost/process/v2/windows/default_launcher.hpp>
#else
#if defined(BOOST_PROCESS_V2_PDFORK)
#include <boost/process/v2/posix/pdfork_launcher.hpp>
#elif defined(BOOST_PROCESS_V2_PIPEFORK)
#include <boost/process/v2/posix/pipe_fork_launcher.hpp>
#else
#include <boost/process/v2/posix/default_launcher.hpp>
#endif
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
#if defined(GENERATING_DOCUMENTATION)
/// The default launcher for processes.
/** This launcher will be used by process if a
* process is launched through the constructor:
*
* @code {.cpp}
* process proc("test", {});
* // equivalent to
* process prod = default_launcher()("test", {});
* @endcode
*
*/
typedef implementation_defined default_process_launcher;
#else
#if defined(BOOST_PROCESS_V2_WINDOWS)
typedef windows::default_launcher default_process_launcher;
#else
#if defined(BOOST_PROCESS_V2_PDFORK)
typedef posix::pdfork_launcher default_process_launcher;
#elif defined(BOOST_PROCESS_V2_PIPEFORK)
typedef posix::pipe_fork_launcher default_process_launcher;
#else
typedef posix::default_launcher default_process_launcher;
#endif
#endif
#endif
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DEFAULT_LAUNCHER_HPP

View File

@@ -0,0 +1,182 @@
// 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_DETAIL_CONFIG_HPP
#define BOOST_PROCESS_V2_DETAIL_CONFIG_HPP
#if defined(BOOST_PROCESS_V2_STANDALONE)
#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) ASIO_COMPLETION_TOKEN_FOR(Sig)
#include <asio/detail/config.hpp>
#include <system_error>
#include <filesystem>
#include <string_view>
#include <iomanip>
#include <optional>
#if defined(ASIO_WINDOWS)
#define BOOST_PROCESS_V2_WINDOWS 1
#endif
#if defined(ASIO_HAS_UNISTD_H)
#define BOOST_PROCESS_V2_POSIX 1
#endif
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace process_v2 {
#define BOOST_PROCESS_V2_END_NAMESPACE }
#define BOOST_PROCESS_V2_NAMESPACE process_v2
namespace asio {}
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace net = ::asio;
BOOST_PROCESS_V2_END_NAMESPACE
#else
#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) BOOST_ASIO_COMPLETION_TOKEN_FOR(Sig)
#include <boost/config.hpp>
#include <boost/io/quoted.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_category.hpp>
#include <boost/system/system_error.hpp>
#include <boost/optional.hpp>
#if defined(BOOST_WINDOWS_API)
#define BOOST_PROCESS_V2_WINDOWS 1
#endif
#if defined(BOOST_POSIX_API)
#define BOOST_PROCESS_V2_POSIX 1
#endif
#if !defined(BOOST_PROCESS_V2_WINDOWS) && !defined(BOOST_POSIX_API)
#error Unsupported operating system
#endif
#if defined(BOOST_PROCESS_USE_STD_FS)
#include <filesystem>
#else
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#endif
#if !defined(BOOST_PROCESS_VERSION)
#define BOOST_PROCESS_VERSION 2
#endif
#if BOOST_PROCESS_VERSION == 1
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace boost { namespace process { namespace v2 {
#else
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace boost { namespace process { inline namespace v2 {
#endif
#define BOOST_PROCESS_V2_END_NAMESPACE } } }
#define BOOST_PROCESS_V2_NAMESPACE boost::process::v2
namespace boost { namespace asio {} }
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace net = ::boost::asio;
BOOST_PROCESS_V2_END_NAMESPACE
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
#if defined(BOOST_PROCESS_STANDALONE)
using std::error_code ;
using std::error_category ;
using std::system_category ;
using std::system_error ;
namespace filesystem = std::filesystem;
using std::quoted;
using std::optional;
#define BOOST_PROCESS_V2_ASSIGN_EC(ec, ...) ec.assign(__VA_ARGS__);
#define BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) \
ec.assign(::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error()); \
#else
using boost::system::error_code ;
using boost::system::error_category ;
using boost::system::system_category ;
using boost::system::system_error ;
using boost::io::quoted;
using boost::optional;
#ifdef BOOST_PROCESS_USE_STD_FS
namespace filesystem = std::filesystem;
#else
namespace filesystem = boost::filesystem;
#endif
#define BOOST_PROCESS_V2_ASSIGN_EC(ec, ...) \
do \
{ \
static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
ec.assign(__VA_ARGS__, &loc##__LINE__); \
} \
while (false)
#define BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) \
do \
{ \
static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
ec.assign(::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(), &loc##__LINE__); \
} \
while (false)
#endif
BOOST_PROCESS_V2_END_NAMESPACE
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_PROCESS_DYN_LINK)
#if defined(BOOST_PROCESS_SOURCE)
#define BOOST_PROCESS_V2_DECL BOOST_SYMBOL_EXPORT
#else
#define BOOST_PROCESS_V2_DECL BOOST_SYMBOL_IMPORT
#endif
#else
#define BOOST_PROCESS_V2_DECL
#endif
#if !defined(BOOST_PROCESS_SOURCE) && !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_PROCESS_NO_LIB)
#define BOOST_LIB_NAME boost_process
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_PROCESS_DYN_LINK)
#define BOOST_DYN_LINK
#endif
#include <boost/config/auto_link.hpp>
#endif
#if defined(BOOST_PROCESS_V2_POSIX)
#if defined(__linux__) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN)
#include <sys/syscall.h>
#if defined(SYS_pidfd_open) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN)
#define BOOST_PROCESS_V2_PIDFD_OPEN 1
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
#endif
#endif
#if defined(__FreeBSD__) && defined(BOOST_PROCESS_V2_ENABLE_PDFORK)
#define BOOST_PROCESS_V2_PDFORK 1
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
#endif
#else
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
#endif
#endif //BOOST_PROCESS_V2_DETAIL_CONFIG_HPP

View File

@@ -0,0 +1,90 @@
//
// process/environment/detail/environment_posix.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_DETAIL_ENVIRONMENT_POSIX_HPP
#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_POSIX_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/cstring_ref.hpp>
#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
namespace environment
{
using char_type = char;
template<typename Char>
using key_char_traits = std::char_traits<Char>;
template<typename Char>
using value_char_traits = std::char_traits<Char>;
constexpr char_type equality_sign = '=';
constexpr char_type delimiter = ':';
namespace detail
{
BOOST_PROCESS_V2_DECL
basic_cstring_ref<char_type, value_char_traits<char>>
get(basic_cstring_ref<char_type, key_char_traits<char_type>> key, error_code & ec);
BOOST_PROCESS_V2_DECL
void set(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
basic_cstring_ref<char_type, value_char_traits<char_type>> value,
error_code & ec);
BOOST_PROCESS_V2_DECL
void unset(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
error_code & ec);
}
using native_handle_type = const char * const *;
using native_iterator = native_handle_type;
namespace detail
{
BOOST_PROCESS_V2_DECL native_handle_type load_native_handle();
struct native_handle_deleter
{
void operator()(native_handle_type) const {}
};
BOOST_PROCESS_V2_DECL native_iterator next(native_handle_type nh);
BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh);
inline const char_type * dereference(native_iterator iterator) {return *iterator;}
BOOST_PROCESS_V2_DECL bool has_x_access(const char * pth);
inline bool is_executable(const filesystem::path & pth, error_code & ec)
{
return filesystem::is_regular_file(pth, ec) && has_x_access(pth.c_str());
}
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif

View File

@@ -0,0 +1,220 @@
//
// process/environment/detail/environment_win.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_DETAIL_ENVIRONMENT_WIN_HPP
#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/cstring_ref.hpp>
#include <cwctype>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace environment
{
using char_type = wchar_t;
template<typename Char>
struct key_char_traits
{
typedef Char char_type;
typedef typename std::char_traits<char_type>::int_type int_type;
typedef typename std::char_traits<char_type>::off_type off_type;
typedef typename std::char_traits<char_type>::pos_type pos_type;
typedef typename std::char_traits<char_type>::state_type state_type;
BOOST_CONSTEXPR static char to_lower(char c) {return std::tolower(to_int_type(c));}
BOOST_CONSTEXPR static wchar_t to_lower(wchar_t c) {return std::towlower(to_int_type(c));}
BOOST_CONSTEXPR static int_type to_lower(int_type i, char ) {return std::tolower(i);}
BOOST_CONSTEXPR static int_type to_lower(int_type i, wchar_t) {return std::towlower(i);}
BOOST_CONSTEXPR static
void assign(char_type& c1, const char_type& c2) BOOST_NOEXCEPT
{
c1 = c2;
}
BOOST_CONSTEXPR static
bool eq(char_type c1, char_type c2) BOOST_NOEXCEPT
{
return to_lower(c1) == to_lower(c2);
}
BOOST_CONSTEXPR static
bool lt(char_type c1, char_type c2) BOOST_NOEXCEPT
{
return to_lower(c1) < to_lower(c2);
}
BOOST_CXX14_CONSTEXPR static
int compare(const char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
{
auto itrs = std::mismatch(s1, s1 + n, s2, &eq);
if (itrs.first == (s1 + n))
return 0;
auto c1 = to_lower(*itrs.first);
auto c2 = to_lower(*itrs.second);
return (c1 < c2 ) ? -1 : 1;
}
static size_t length(const char* s) BOOST_NOEXCEPT { return std::strlen(s); }
static size_t length(const wchar_t* s) BOOST_NOEXCEPT { return std::wcslen(s); }
BOOST_CXX14_CONSTEXPR static
const char_type* find(const char_type* s, size_t n, const char_type& a) BOOST_NOEXCEPT
{
const char_type u = to_lower(a);
return std::find_if(s, s + n, [u](char_type c){return to_lower(c) == u;});
}
BOOST_CXX14_CONSTEXPR static
char_type* move(char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
{
if (s1 < s2)
return std::move(s2, s2 + n, s1);
else
return std::move_backward(s2, s2 + n, s1 + n);
}
BOOST_CONSTEXPR static
char_type* copy(char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
{
return std::copy(s2, s2 + n, s1);
}
BOOST_CXX14_CONSTEXPR static
char_type* assign(char_type* s, size_t n, char_type a) BOOST_NOEXCEPT
{
std::fill(s, s + n, a);
return s +n;
}
BOOST_CONSTEXPR static
int_type not_eof(int_type c) BOOST_NOEXCEPT
{
return eq_int_type(c, eof()) ? ~eof() : c;
}
BOOST_CONSTEXPR static
char_type to_char_type(int_type c) BOOST_NOEXCEPT
{
return char_type(c);
}
BOOST_CONSTEXPR static
int_type to_int_type(char c) BOOST_NOEXCEPT
{
return int_type((unsigned char)c);
}
BOOST_CONSTEXPR static
int_type to_int_type(wchar_t c) BOOST_NOEXCEPT
{
return int_type((wchar_t)c);
}
BOOST_CONSTEXPR static
bool eq_int_type(int_type c1, int_type c2) BOOST_NOEXCEPT
{
return to_lower(c1, char_type()) == to_lower(c2, char_type());
}
BOOST_CONSTEXPR static inline int_type eof() BOOST_NOEXCEPT
{
return int_type(EOF);
}
};
namespace detail
{
template<typename Char>
std::size_t hash_step(std::size_t prev, Char c, key_char_traits<Char>)
{
return prev ^ (key_char_traits<Char>::to_lower(c) << 1);
}
}
template<typename Char>
using value_char_traits = std::char_traits<Char>;
BOOST_CONSTEXPR static char_type equality_sign = L'=';
BOOST_CONSTEXPR static char_type delimiter = L';';
using native_handle_type = wchar_t*;
using native_iterator = const wchar_t*;
namespace detail
{
BOOST_PROCESS_V2_DECL
std::basic_string<wchar_t, value_char_traits<wchar_t>> get(
basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
error_code & ec);
BOOST_PROCESS_V2_DECL
void set(basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
basic_cstring_ref<wchar_t, value_char_traits<wchar_t>> value,
error_code & ec);
BOOST_PROCESS_V2_DECL
void unset(basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
error_code & ec);
BOOST_PROCESS_V2_DECL
std::basic_string<char, value_char_traits<char>> get(
basic_cstring_ref<char, key_char_traits<char>> key,
error_code & ec);
BOOST_PROCESS_V2_DECL
void set(basic_cstring_ref<char, key_char_traits<char>> key,
basic_cstring_ref<char, value_char_traits<char>> value,
error_code & ec);
BOOST_PROCESS_V2_DECL
void unset(basic_cstring_ref<char, key_char_traits<char>> key,
error_code & ec);
BOOST_PROCESS_V2_DECL native_handle_type load_native_handle();
struct native_handle_deleter
{
native_handle_deleter() = default;
native_handle_deleter(const native_handle_deleter& ) = default;
BOOST_PROCESS_V2_DECL void operator()(native_handle_type nh) const;
};
inline const char_type * dereference(native_iterator iterator) {return iterator;}
BOOST_PROCESS_V2_DECL native_iterator next(native_iterator nh);
BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh);
BOOST_PROCESS_V2_DECL bool is_exec_type(const wchar_t * pth);
inline bool is_executable(const filesystem::path & pth, error_code & ec)
{
return filesystem::is_regular_file(pth, ec) && is_exec_type(pth.c_str());
}
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP

View File

@@ -0,0 +1,22 @@
// 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_DETAIL_LAST_ERROR_HPP
#define BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP
#include <boost/process/v2/detail/config.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_PROCESS_V2_DECL error_code get_last_error();
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP

View File

@@ -0,0 +1,355 @@
// 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_DETAIL_PROCESS_HANDLE_FD_HPP
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_HPP
#include <boost/process/v2/detail/config.hpp>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/any_io_executor.hpp>
#include <asio/compose.hpp>
#include <asio/posix/basic_stream_descriptor.hpp>
#include <asio/post.hpp>
#else
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/posix/basic_stream_descriptor.hpp>
#include <boost/asio/post.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
template<typename Executor = net::any_io_executor>
struct basic_process_handle_fd
{
using native_handle_type = int;
typedef Executor executor_type;
executor_type get_executor()
{ return descriptor_.get_executor(); }
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle_fd<Executor1> other;
};
template<typename ExecutionContext>
basic_process_handle_fd(ExecutionContext &context,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value>::type * = nullptr)
: pid_(-1), descriptor_(context)
{
}
basic_process_handle_fd(executor_type executor)
: pid_(-1), descriptor_(executor)
{
}
basic_process_handle_fd(executor_type executor, pid_type pid)
: pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0))
{
if (descriptor_.native_handle() == -1)
detail::throw_error(detail::get_last_error(), "wait(pid)");
}
basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle)
: pid_(pid), descriptor_(executor, process_handle)
{
}
basic_process_handle_fd(basic_process_handle_fd &&handle)
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
{
handle.pid_ = -1;
}
template<typename Executor1>
basic_process_handle_fd(basic_process_handle_fd<Executor1> &&handle)
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
{
handle.pid_ = -1;
}
basic_process_handle_fd& operator=(basic_process_handle_fd &&handle)
{
pid_ = handle.pid_;
descriptor_ = std::move(handle.descriptor_);
handle.pid_ = -1;
return *this;
}
pid_type id() const
{ return pid_; }
native_handle_type native_handle() {return pid_;}
void terminate_if_running(error_code &)
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void terminate_if_running()
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void wait(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
while (::waitpid(pid_, &exit_status, 0) < 0)
{
if (errno != EINTR)
{
ec = get_last_error();
break;
}
}
}
void wait(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
wait(exit_status, ec);
if (ec)
detail::throw_error(ec, "wait(pid)");
}
void interrupt(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGINT) == -1)
ec = get_last_error();
}
void interrupt()
{
if (pid_ <= 0)
return;
error_code ec;
interrupt(ec);
if (ec)
detail::throw_error(ec, "interrupt");
}
void request_exit(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGTERM) == -1)
ec = get_last_error();
}
void request_exit()
{
if (pid_ <= 0)
return;
error_code ec;
request_exit(ec);
if (ec)
detail::throw_error(ec, "request_exit");
}
void suspend()
{
if (pid_ <= 0)
return ;
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
void suspend(error_code &ec)
{
if (pid_ <= 0)
return ;
if (::kill(pid_, SIGSTOP) == -1)
ec = get_last_error();
}
void resume()
{
if (pid_ <= 0)
return ;
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
void resume(error_code &ec)
{
if (pid_ <= 0)
return ;
if (::kill(pid_, SIGCONT) == -1)
ec = get_last_error();
}
void terminate(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGKILL) == -1)
ec = get_last_error();
else
wait(exit_status, ec);
}
void terminate(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
terminate(exit_status, ec);
if (ec)
detail::throw_error(ec, "terminate");
}
bool running(native_exit_code_type &exit_code, error_code & ec)
{
if (pid_ <= 0)
return false;
int code = 0;
int res = ::waitpid(pid_, &code, WNOHANG);
if (res == -1)
ec = get_last_error();
else if (res == 0)
return true;
else
{
ec.clear();
exit_code = code;
}
return false;
}
bool running(native_exit_code_type &exit_code)
{
if (pid_ <= 0)
return false;
error_code ec;
bool res = running(exit_code, ec);
if (ec)
detail::throw_error(ec, "is_running");
return res;
}
bool is_open() const
{
return pid_ != -1;
}
private:
template<typename>
friend
struct basic_process_handle_fd;
pid_type pid_ = -1;
net::posix::basic_stream_descriptor<Executor> descriptor_;
struct async_wait_op_
{
net::posix::basic_descriptor<Executor> &descriptor;
pid_type pid_;
native_exit_code_type & exit_code;
template<typename Self>
void operator()(Self &&self)
{
self.reset_cancellation_state(asio::enable_total_cancellation());
error_code ec;
int wait_res = -1;
if (pid_ <= 0) // error, complete early
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::bad_descriptor);
else if (process_is_running(exit_code))
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
ec = get_last_error();
}
if (!ec && (wait_res == 0))
{
descriptor.async_wait(net::posix::descriptor_base::wait_read, std::move(self));
return;
}
struct completer
{
error_code ec;
typename std::decay<Self>::type self;
void operator()()
{
self.complete(ec);
}
};
net::dispatch(
net::get_associated_immediate_executor(self, descriptor.get_executor()),
completer{ec, std::move(self)});
}
template<typename Self>
void operator()(Self &&self, error_code ec, int = 0)
{
if (!ec && process_is_running(exit_code))
if (::waitpid(pid_, &exit_code, 0) == -1)
ec = get_last_error();
std::move(self).complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, pid_, exit_code}, handler, descriptor_))
{
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, pid_, exit_code}, handler, descriptor_);
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP

View File

@@ -0,0 +1,413 @@
// 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_DETAIL_PROCESS_HANDLE_FD_OR_SIGNAL_HPP
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_OR_SIGNAL_HPP
#include <boost/process/v2/detail/config.hpp>
#include <sys/types.h>
#include <sys/wait.h>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/any_io_executor.hpp>
#include <asio/append.hpp>
#include <asio/associated_immediate_executor.hpp>
#include <asio/compose.hpp>
#include <asio/dispatch.hpp>
#include <asio/posix/basic_stream_descriptor.hpp>
#include <asio/post.hpp>
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
#include <asio/signal_set.hpp>
#endif
#else
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/append.hpp>
#include <boost/asio/associated_immediate_executor.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/posix/basic_stream_descriptor.hpp>
#include <boost/asio/post.hpp>
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
#include <boost/asio/signal_set.hpp>
#endif
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
template<typename Executor = net::any_io_executor>
struct basic_process_handle_fd_or_signal
{
using native_handle_type = int;
typedef Executor executor_type;
executor_type get_executor()
{ return descriptor_.get_executor(); }
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle_fd_or_signal<Executor1> other;
};
template<typename ExecutionContext>
basic_process_handle_fd_or_signal(ExecutionContext &context,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type * = nullptr)
: pid_(-1), descriptor_(context)
{
}
template<typename ExecutionContext>
basic_process_handle_fd_or_signal(ExecutionContext &context,
pid_type pid,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type * = nullptr)
: pid_(pid), descriptor_(context)
{
}
template<typename ExecutionContext>
basic_process_handle_fd_or_signal(ExecutionContext &context,
pid_type pid, native_handle_type process_handle,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type * = nullptr)
: pid_(pid), descriptor_(context, process_handle)
{
}
basic_process_handle_fd_or_signal(Executor executor)
: pid_(-1), descriptor_(executor)
{
}
basic_process_handle_fd_or_signal(Executor executor, pid_type pid)
: pid_(pid), descriptor_(executor)
{
}
basic_process_handle_fd_or_signal(Executor executor, pid_type pid, native_handle_type process_handle)
: pid_(pid), descriptor_(executor, process_handle)
{
}
basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal &&handle)
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
{
handle.pid_ = -1;
}
// Warn: does not change the executor of the signal-set.
basic_process_handle_fd_or_signal& operator=(basic_process_handle_fd_or_signal &&handle)
{
pid_ = handle.pid_;
descriptor_ = std::move(handle.descriptor_);
handle.pid_ = -1;
return *this;
}
template<typename Executor1>
basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal<Executor1> &&handle)
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
{
handle.pid_ = -1;
}
pid_type id() const
{ return pid_; }
native_handle_type native_handle() {return pid_;}
void terminate_if_running(error_code &)
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void terminate_if_running()
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void wait(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
while (::waitpid(pid_, &exit_status, 0) < 0)
{
if (errno != EINTR)
{
ec = get_last_error();
break;
}
}
}
void wait(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
wait(exit_status, ec);
if (ec)
detail::throw_error(ec, "wait(pid)");
}
void interrupt(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGINT) == -1)
ec = get_last_error();
}
void interrupt()
{
if (pid_ <= 0)
return;
error_code ec;
interrupt(ec);
if (ec)
detail::throw_error(ec, "interrupt");
}
void request_exit(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGTERM) == -1)
ec = get_last_error();
}
void request_exit()
{
if (pid_ <= 0)
return;
error_code ec;
request_exit(ec);
if (ec)
detail::throw_error(ec, "request_exit");
}
void suspend()
{
if (pid_ <= 0)
return ;
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
void suspend(error_code &ec)
{
if (pid_ <= 0)
return ;
if (::kill(pid_, SIGSTOP) == -1)
ec = get_last_error();
}
void resume()
{
if (pid_ <= 0)
return ;
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
void resume(error_code &ec)
{
if (pid_ <= 0)
return ;
if (::kill(pid_, SIGCONT) == -1)
ec = get_last_error();
}
void terminate(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGKILL) == -1)
ec = get_last_error();
else
wait(exit_status, ec);
}
void terminate(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
terminate(exit_status, ec);
if (ec)
detail::throw_error(ec, "terminate");
}
bool running(native_exit_code_type &exit_code, error_code & ec)
{
if (pid_ <= 0)
return false;
int code = 0;
int res = ::waitpid(pid_, &code, WNOHANG);
if (res == -1)
ec = get_last_error();
else if (res == 0)
return true;
else
{
ec.clear();
exit_code = code;
}
return false;
}
bool running(native_exit_code_type &exit_code)
{
if (pid_ <= 0)
return false;
error_code ec;
bool res = running(exit_code, ec);
if (ec)
detail::throw_error(ec, "is_running");
return res;
}
bool is_open() const
{
return pid_ != -1;
}
private:
template<typename>
friend
struct basic_process_handle_fd_or_signal;
pid_type pid_ = -1;
net::posix::basic_stream_descriptor<Executor> descriptor_;
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
net::basic_signal_set<Executor> signal_set_{descriptor_.get_executor(), SIGCHLD};
#else
int signal_set_;
#endif
struct async_wait_op_
{
net::posix::basic_descriptor<Executor> &descriptor;
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
net::basic_signal_set<Executor> &handle;
#else
int dummy;
#endif
pid_type pid_;
native_exit_code_type & exit_code;
bool needs_post = true;
template<typename Self>
void operator()(Self && self)
{
self.reset_cancellation_state(asio::enable_total_cancellation());
(*this)(std::move(self), error_code{});
}
template<typename Self>
void operator()(Self &&self, error_code ec, int = 0)
{
int wait_res = -1;
if (pid_ <= 0) // error, complete early
ec = net::error::bad_descriptor;
else if (process_is_running(exit_code))
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
ec = get_last_error();
}
if (!ec && (wait_res == 0))
{
if (descriptor.is_open())
{
needs_post = false;
descriptor.async_wait(
net::posix::descriptor_base::wait_read,
std::move(self));
return;
}
else
{
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
needs_post = false;
handle.async_wait(std::move(self));
return;
#else
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported);
#endif
}
}
if (needs_post)
{
auto exec = net::get_associated_immediate_executor(self, descriptor.get_executor());
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
}
else
{
auto exec = net::get_associated_executor(self);
net::dispatch(exec, net::append(std::move(self), exit_code, ec));
}
}
template<typename Self>
void operator()(Self &&self, native_exit_code_type code, error_code ec)
{
self.complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_))
{
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{descriptor_, signal_set_, pid_, exit_code}, handler, descriptor_);
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP

View File

@@ -0,0 +1,387 @@
// 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_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
#include <boost/process/v2/detail/config.hpp>
#include <sys/types.h>
#include <sys/wait.h>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/any_io_executor.hpp>
#include <asio/append.hpp>
#include <asio/associated_immediate_executor.hpp>
#include <asio/compose.hpp>
#include <asio/dispatch.hpp>
#include <asio/post.hpp>
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
#include <asio/signal_set.hpp>
#endif
#else
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/append.hpp>
#include <boost/asio/associated_immediate_executor.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
#include <boost/asio/signal_set.hpp>
#endif
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
template<typename Executor = net::any_io_executor>
struct basic_process_handle_signal
{
struct native_handle_type
{
native_handle_type() = delete;
native_handle_type(const native_handle_type & ) = delete;
~native_handle_type() = default;
};
typedef Executor executor_type;
executor_type get_executor()
{ return signal_set_.get_executor(); }
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle_signal<Executor1> other;
};
template<typename ExecutionContext>
basic_process_handle_signal(ExecutionContext &context,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type * = nullptr)
: pid_(-1), signal_set_(context, SIGCHLD)
{
}
basic_process_handle_signal(Executor executor)
: pid_(-1), signal_set_(executor, SIGCHLD)
{
}
basic_process_handle_signal(Executor executor, pid_type pid)
: pid_(pid), signal_set_(executor, SIGCHLD)
{
}
basic_process_handle_signal(basic_process_handle_signal && handle)
: pid_(handle.pid_), signal_set_(handle.signal_set_.get_executor(), SIGCHLD)
{
handle.pid_ = -1;
}
basic_process_handle_signal& operator=(basic_process_handle_signal && handle)
{
pid_ = handle.id();
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
signal_set_.~basic_signal_set();
using ss = net::basic_signal_set<Executor>;
new (&signal_set_) ss(handle.get_executor(), SIGCHLD);
#else
signal_set_.executor = handle.signal_set_.executor;
#endif
handle.pid_ = -1;
return *this;
}
template<typename Executor1>
basic_process_handle_signal(basic_process_handle_signal<Executor1> && handle)
: pid_(handle.pid_), signal_set_(Executor1(handle.signal_set_.get_executor()), SIGCHLD)
{
handle.pid_ = -1;
}
pid_type id() const { return pid_; }
native_handle_type native_handle() {return {};}
void terminate_if_running(error_code &)
{
terminate_if_running();
}
void terminate_if_running()
{
if (pid_ <= 0)
return;
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
{
::kill(pid_, SIGKILL);
::waitpid(pid_, nullptr, 0);
}
}
void wait(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
while (::waitpid(pid_, &exit_status, 0) < 0)
{
if (errno != EINTR)
{
ec = get_last_error();
break;
}
}
}
void wait(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
wait(exit_status, ec);
if (ec)
detail::throw_error(ec, "wait(pid)");
}
void interrupt(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGINT) == -1)
ec = get_last_error();
}
void interrupt()
{
if (pid_ <= 0)
return;
error_code ec;
interrupt(ec);
if (ec)
detail::throw_error(ec, "interrupt");
}
void request_exit(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGTERM) == -1)
ec = get_last_error();
}
void request_exit()
{
if (pid_ <= 0)
return;
error_code ec;
request_exit(ec);
if (ec)
detail::throw_error(ec, "request_exit");
}
void suspend()
{
if (pid_ <= 0)
return;
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
void suspend(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGSTOP) == -1)
ec = get_last_error();
}
void resume()
{
if (pid_ <= 0)
return;
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
void resume(error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGCONT) == -1)
ec = get_last_error();
}
void terminate(native_exit_code_type &exit_status, error_code &ec)
{
if (pid_ <= 0)
return;
if (::kill(pid_, SIGKILL) == -1)
ec = get_last_error();
else
wait(exit_status, ec);
}
void terminate(native_exit_code_type &exit_status)
{
if (pid_ <= 0)
return;
error_code ec;
terminate(exit_status, ec);
if (ec)
detail::throw_error(ec, "terminate");
}
bool running(native_exit_code_type &exit_code, error_code & ec)
{
if (pid_ <= 0)
return false;
int code = 0;
int res = ::waitpid(pid_, &code, WNOHANG);
if (res == -1)
ec = get_last_error();
else if (res == 0)
return true;
else
{
ec.clear();
exit_code = code;
}
return false;
}
bool running(native_exit_code_type &exit_code)
{
if (pid_ <= 0)
return false;
error_code ec;
bool res = running(exit_code, ec);
if (ec)
detail::throw_error(ec, "is_running");
return res;
}
bool is_open() const
{
return pid_ != -1;
}
private:
template<typename>
friend struct basic_process_handle_signal;
pid_type pid_ = -1;
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
net::basic_signal_set<Executor> signal_set_;
#else
struct signal_set_dummy_
{
signal_set_dummy_(signal_set_dummy_ &&) = default;
signal_set_dummy_(const signal_set_dummy_ &) = default;
Executor executor;
using executor_type = Executor;
executor_type get_executor() {return executor;}
signal_set_dummy_(Executor executor, int) : executor(std::move(executor)) {}
};
signal_set_dummy_ signal_set_;
#endif
struct async_wait_op_
{
#if !defined(BOOST_PROCESS_V2_DISABLE_SIGNALSET)
net::basic_signal_set<Executor> &handle;
pid_type pid_;
native_exit_code_type & exit_code;
template<typename Self>
void operator()(Self &&self)
{
self.reset_cancellation_state(asio::enable_total_cancellation());
handle.async_wait(std::move(self));
handle.cancel();
// we cancel so we end up on the signal-sets executor
}
template<typename Self>
void operator()(Self &&self, error_code ec, int /*sig*/)
{
if (ec == net::error::operation_aborted &&
self.get_cancellation_state().cancelled()
== net::cancellation_type::none)
ec.clear();
int wait_res = -1;
if (pid_ <= 0) // error, complete early
ec = net::error::bad_descriptor;
else if (!ec && process_is_running(exit_code))
{
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
if (wait_res == -1)
ec = get_last_error();
}
if (!ec && (wait_res == 0))
{
handle.async_wait(std::move(self));
return;
}
const auto exec = self.get_executor();
net::dispatch(exec, net::append(std::move(self), ec));
}
#else
signal_set_dummy_ dummy_;
pid_t pid;
template<typename Self>
void operator()(Self &&self)
{
auto exec = net::get_associated_immediate_executor(self, dummy_.get_executor());
error_code ec;
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported);
net::dispatch(exec, net::append(std::move(self), native_exit_code_type(), ec));
}
#endif
template<typename Self>
void operator()(Self &&self, error_code ec)
{
self.complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_))
{
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{signal_set_, pid_, exit_code}, handler, signal_set_);
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP

View File

@@ -0,0 +1,326 @@
// 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_DETAIL_PROCESS_HANDLE_WINDOWS_HPP
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/any_io_executor.hpp>
#include <asio/compose.hpp>
#include <asio/windows/basic_object_handle.hpp>
#else
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/windows/basic_object_handle.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_PROCESS_V2_DECL void get_exit_code_( void * handle, native_exit_code_type & exit_code, error_code & ec);
BOOST_PROCESS_V2_DECL void * open_process_(pid_type pid);
BOOST_PROCESS_V2_DECL void terminate_if_running_(void * handle);
BOOST_PROCESS_V2_DECL bool check_handle_(void* handle, error_code & ec);
BOOST_PROCESS_V2_DECL bool check_pid_(pid_type pid_, error_code & ec);
BOOST_PROCESS_V2_DECL void interrupt_(pid_type pid_, error_code & ec);
BOOST_PROCESS_V2_DECL void suspend_(void * handle, error_code & ec);
BOOST_PROCESS_V2_DECL void resume_(void * handle, error_code & ec);
BOOST_PROCESS_V2_DECL void terminate_(void * handle, error_code & ec, native_exit_code_type & exit_code);
BOOST_PROCESS_V2_DECL void request_exit_(pid_type pid_, error_code & ec);
BOOST_PROCESS_V2_DECL void check_running_(void* handle, error_code & ec, native_exit_code_type & exit_status);
template<typename Executor = net::any_io_executor>
struct basic_process_handle_win
{
typedef net::windows::basic_object_handle<Executor> handle_type;
typedef typename handle_type::native_handle_type native_handle_type;
typedef Executor executor_type;
executor_type get_executor()
{ return handle_.get_executor(); }
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle_win<Executor1> other;
};
template<typename ExecutionContext>
basic_process_handle_win(ExecutionContext &context,
typename std::enable_if<
std::is_convertible<ExecutionContext &,
net::execution_context &>::value
>::type = 0)
: pid_(0), handle_(context)
{
}
basic_process_handle_win(Executor executor)
: pid_(0), handle_(executor)
{
}
basic_process_handle_win(Executor executor, pid_type pid)
: pid_(pid), handle_(executor, detail::open_process_(pid))
{
}
basic_process_handle_win(Executor executor, pid_type pid, native_handle_type process_handle)
: pid_(pid), handle_(executor, process_handle)
{
}
template<typename Executor1>
basic_process_handle_win(basic_process_handle_win<Executor1> && other)
: pid_(other.pid_), handle_(std::move(other.handle_))
{
other.pid_ = static_cast<DWORD>(-1);
}
basic_process_handle_win(basic_process_handle_win && other)
: pid_(other.pid_), handle_(std::move(other.handle_))
{
other.pid_ = static_cast<DWORD>(-1);
}
basic_process_handle_win& operator=(basic_process_handle_win && other)
{
pid_ = other.pid_;
handle_ = std::move(other.handle_);
other.pid_ = static_cast<DWORD>(-1);
return *this;
}
template<typename Executor1>
basic_process_handle_win& operator=(basic_process_handle_win<Executor1> && other)
{
pid_ = other.pid_;
handle_ = std::move(other.handle_);
other.pid_ = static_cast<DWORD>(-1);
return *this;
}
~basic_process_handle_win()
{
if (handle_.is_open())
{
error_code ec;
handle_.close(ec);
}
}
native_handle_type native_handle()
{ return handle_.native_handle(); }
pid_type id() const
{ return pid_; }
void terminate_if_running(error_code &)
{
detail::terminate_if_running_(handle_.native_handle());
}
void terminate_if_running()
{
detail::terminate_if_running_(handle_.native_handle());
}
void wait(native_exit_code_type &exit_status, error_code &ec)
{
if (!detail::check_handle_(handle_.native_handle(), ec))
return;
handle_.wait(ec);
if (!ec)
detail::get_exit_code_(handle_.native_handle(), exit_status, ec);
}
void wait(native_exit_code_type &exit_status)
{
error_code ec;
wait(exit_status, ec);
if (ec)
detail::throw_error(ec, "wait(pid)");
}
void interrupt(error_code &ec)
{
if (!detail::check_pid_(pid_, ec))
return;
detail::interrupt_(pid_, ec);
}
void interrupt()
{
error_code ec;
interrupt(ec);
if (ec)
detail::throw_error(ec, "interrupt");
}
void request_exit(error_code &ec)
{
if (!detail::check_pid_(pid_, ec))
return;
detail::request_exit_(pid_, ec);
}
void request_exit()
{
error_code ec;
request_exit(ec);
if (ec)
detail::throw_error(ec, "request_exit");
}
void suspend(error_code &ec)
{
detail::suspend_(handle_.native_handle(), ec);
}
void suspend()
{
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
void resume(error_code &ec)
{
detail::resume_(handle_.native_handle(), ec);
}
void resume()
{
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
void terminate(native_exit_code_type &exit_status, error_code &ec)
{
if (!detail::check_handle_(handle_.native_handle(), ec))
return;
detail::terminate_(handle_.native_handle(), ec, exit_status);
if (!ec)
wait(exit_status, ec);
}
void terminate(native_exit_code_type &exit_status)
{
error_code ec;
terminate(exit_status, ec);
if (ec)
detail::throw_error(ec, "terminate");
}
bool running(native_exit_code_type &exit_code, error_code & ec)
{
if (!detail::check_handle_(handle_.native_handle(), ec))
return false;
native_exit_code_type code;
//single value, not needed in the winapi.
detail::check_running_(handle_.native_handle(), ec, code);
if (ec)
return false;
if (process_is_running(code))
return true;
else
exit_code = code;
return false;
}
bool running(native_exit_code_type &exit_code)
{
error_code ec;
bool res = running(exit_code, ec);
if (ec)
detail::throw_error(ec, "is_running");
return res;
}
bool is_open() const
{
return handle_.is_open();
}
template<typename>
friend struct basic_process_handle_win;
private:
pid_type pid_;
handle_type handle_;
struct async_wait_op_
{
handle_type &handle;
native_exit_code_type & exit_code;
template<typename Self>
void operator()(Self &&self)
{
self.reset_cancellation_state(asio::enable_total_cancellation());
auto sl = self.get_cancellation_state().slot();
auto & h = handle;
if (sl.is_connected())
sl.assign(
[&h](asio::cancellation_type ct)
{
error_code ec;
h.cancel(ec);
});
handle.async_wait(std::move(self));
}
template<typename Self>
void operator()(Self &&self, error_code ec)
{
if (ec == asio::error::operation_aborted && !self.get_cancellation_state().cancelled())
return handle.async_wait(std::move(self));
if (!ec && process_is_running(exit_code)) // exit_code could be set by another call to wait.
detail::get_exit_code_(handle.native_handle(), exit_code, ec);
std::move(self).complete(ec);
}
};
public:
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(native_exit_code_type & exit_code,
WaitHandler &&handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{handle_, exit_code}, handler, handle_))
{
return net::async_compose<WaitHandler, void(error_code)>(
async_wait_op_{handle_, exit_code}, handler, handle_
);
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP

View File

@@ -0,0 +1,33 @@
// 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_DETAIL_THROW_ERROR_HPP
#define BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP
#include <boost/process/v2/detail/config.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_PROCESS_V2_DECL void do_throw_error(const error_code& err);
BOOST_PROCESS_V2_DECL void do_throw_error(const error_code& err, const char* location);
inline void throw_error(const error_code& err)
{
if (err)
do_throw_error(err);
}
inline void throw_error(const error_code& err, const char* location)
{
if (err)
do_throw_error(err, location);
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP

View File

@@ -0,0 +1,39 @@
// 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_DETAIL_THROW_EXCEPTION_HPP
#define BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP
#include <boost/process/v2/detail/config.hpp>
#if !defined(BOOST_PROCESS_V2_STANDALONE)
#include <boost/throw_exception.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
#if defined(BOOST_PROCESS_V2_STANDALONE)
template <typename Exception>
inline void throw_exception(const Exception& e)
{
throw e;
}
#else
using boost::throw_exception;
#endif
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP

View File

@@ -0,0 +1,94 @@
// 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_DETAIL_UTF8_HPP
#define BOOST_PROCESS_V2_DETAIL_UTF8_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
BOOST_PROCESS_V2_DECL std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec);
BOOST_PROCESS_V2_DECL std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec);
BOOST_PROCESS_V2_DECL std::size_t convert_to_utf8(const wchar_t * in, std::size_t size,
char * out, std::size_t max_size, error_code & ec);
BOOST_PROCESS_V2_DECL std::size_t convert_to_wide(const char * in, std::size_t size,
wchar_t * out, std::size_t max_size, error_code & ec);
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
typename Allocator = std::allocator<CharOut>, typename CharIn,
typename = typename std::enable_if<std::is_same<CharOut, CharIn>::value>::type>
std::basic_string<CharOut, Traits, Allocator> conv_string(
const CharIn * data, std::size_t size,
const Allocator allocator = Allocator{})
{
return std::basic_string<CharOut, Traits, Allocator>(data, size, allocator);
}
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
typename Allocator = std::allocator<CharOut>,
typename = typename std::enable_if<std::is_same<CharOut, char>::value>::type>
std::basic_string<CharOut, Traits, Allocator> conv_string(
const wchar_t * data, std::size_t size,
const Allocator allocator = Allocator{})
{
error_code ec;
const auto req_size = size_as_utf8(data, size, ec);
if (ec)
detail::throw_error(ec, "size_as_utf8");
std::basic_string<CharOut, Traits, Allocator> res(allocator);
res.resize(req_size);
if (req_size == 0)
return res;
auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec);
if (ec)
detail::throw_error(ec, "convert_to_utf8");
res.resize(res_size);
return res;
}
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
typename Allocator = std::allocator<CharOut>,
typename = typename std::enable_if<std::is_same<CharOut, wchar_t>::value>::type>
std::basic_string<CharOut, Traits, Allocator> conv_string(
const char * data, std::size_t size,
const Allocator allocator = Allocator{})
{
error_code ec;
const auto req_size = size_as_wide(data, size, ec);
if (ec)
detail::throw_error(ec, "size_as_wide");
std::basic_string<CharOut, Traits, Allocator> res(allocator);
res.resize(req_size);
if (req_size == 0)
return res;
auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec);
if (ec)
detail::throw_error(ec, "convert_to_wide");
res.resize(res_size);
return res;
}
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_DETAIL_UTF8_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2021 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_ERROR_HPP
#define BOOST_PROCESS_V2_ERROR_HPP
#include <boost/process/v2/detail/config.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace error
{
/// Errors used for utf8 <-> UCS-2 conversions.
enum utf8_conv_error
{
insufficient_buffer = 1,
invalid_character,
};
extern BOOST_PROCESS_V2_DECL const error_category& get_utf8_category();
static const error_category& utf8_category = get_utf8_category();
extern BOOST_PROCESS_V2_DECL const error_category& get_exit_code_category();
/// An error category that can be used to interpret exit codes of subprocesses.
/** Currently not used by boost.process, but it might be in the future.
*
* void run_my_process(filesystem::path pt, error_code & ec)
* {
* process proc(pt, {});
* proc.wait();
* ec.assign(proc.native_exit_code(), error::get_exit_code_category());
* }
*
* */
static const error_category& exit_code_category = get_exit_code_category();
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_ERROR_HPP

View File

@@ -0,0 +1,121 @@
// 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_EXECUTE_HPP
#define BOOST_PROCESS_V2_EXECUTE_HPP
#include <boost/process/v2/process.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/bind_cancellation_slot.hpp>
#else
#include <boost/asio/bind_cancellation_slot.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
/**
* @brief Run a process and wait for it to complete.
*
* @tparam Executor The asio executor of the process handle
* @param proc The process to be run.
* @return int The exit code of the process
* @exception system_error An error that might have occurred during the wait.
*/
template<typename Executor>
inline int execute(basic_process<Executor> proc)
{
return proc.wait();
}
/** \overload int execute(const basic_process<Executor> proc) */
template<typename Executor>
inline int execute(basic_process<Executor> proc, error_code & ec)
{
return proc.wait(ec);
}
namespace detail
{
template<typename Executor>
struct execute_op
{
std::unique_ptr<basic_process<Executor>> proc;
struct cancel
{
using cancellation_type = net::cancellation_type;
basic_process<Executor> * proc;
cancel(basic_process<Executor> * proc) : proc(proc) {}
void operator()(cancellation_type tp)
{
error_code ign;
if ((tp & cancellation_type::total) != cancellation_type::none)
proc->interrupt(ign);
else if ((tp & cancellation_type::partial) != cancellation_type::none)
proc->request_exit(ign);
else if ((tp & cancellation_type::terminal) != cancellation_type::none)
proc->terminate(ign);
}
};
template<typename Self>
void operator()(Self && self)
{
self.reset_cancellation_state(net::enable_total_cancellation());
net::cancellation_slot s = self.get_cancellation_state().slot();
if (s.is_connected())
s.emplace<cancel>(proc.get());
auto pro_ = proc.get();
pro_->async_wait(
net::bind_cancellation_slot(
net::cancellation_slot(),
std::move(self)));
}
template<typename Self>
void operator()(Self && self, error_code ec, int res)
{
self.get_cancellation_state().slot().clear();
self.complete(ec, res);
}
};
}
/// Execute a process asynchronously
/** This function asynchronously for a process to complete.
*
* Cancelling the execution will signal the child process to exit
* with the following interpretations:
*
* - cancellation_type::total -> interrupt
* - cancellation_type::partial -> request_exit
* - cancellation_type::terminal -> terminate
*
* It is to note that `async_execute` will us the lowest selected cancellation
* type. A subprocess might ignore anything not terminal.
*/
template<typename Executor = net::any_io_executor,
BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, int))
WaitHandler = net::default_completion_token_t<Executor>>
inline
auto async_execute(basic_process<Executor> proc,
WaitHandler && handler = net::default_completion_token_t<Executor>())
-> decltype(net::async_compose<WaitHandler, void(error_code, int)>(
detail::execute_op<Executor>{nullptr}, handler, std::declval<Executor>()))
{
std::unique_ptr<basic_process<Executor>> pro_(new basic_process<Executor>(std::move(proc)));
auto exec = pro_->get_executor();
return net::async_compose<WaitHandler, void(error_code, int)>(
detail::execute_op<Executor>{std::move(pro_)}, handler, exec);
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_EXECUTE_HPP

View File

@@ -0,0 +1,135 @@
//
// process/exit_code.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_EXIT_CODE_HPP
#define BOOST_PROCESS_V2_EXIT_CODE_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/error.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/associator.hpp>
#include <asio/async_result.hpp>
#else
#include <boost/asio/associator.hpp>
#include <boost/asio/async_result.hpp>
#endif
#if defined(BOOST_PROCESS_V2_POSIX)
#include <sys/wait.h>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
#if defined(GENERATING_DOCUMENTATION)
/// The native exit-code type, usually an integral value
/** The OS may have a value different from `int` to represent
* the exit codes of subprocesses. It might also
* contain additional information.
*/
typedef implementation_defined native_exit_code_type;
/// Check if the native exit code indicates the process is still running
bool process_is_running(native_exit_code_type code);
/// Obtain the portable part of the exit code, i.e. what the subprocess has returned from main.
int evaluate_exit_code(native_exit_code_type code);
#else
#if defined(BOOST_PROCESS_V2_WINDOWS)
typedef unsigned long native_exit_code_type;
namespace detail
{
constexpr native_exit_code_type still_active = 259u;
}
inline bool process_is_running(native_exit_code_type code)
{
return code == detail::still_active;
}
inline int evaluate_exit_code(native_exit_code_type code)
{
return static_cast<int>(code);
}
#else
typedef int native_exit_code_type;
namespace detail
{
constexpr native_exit_code_type still_active = 0x17f;
static_assert(WIFSTOPPED(still_active), "Expected still_active to indicate WIFSTOPPED");
static_assert(!WIFEXITED(still_active), "Expected still_active to not indicate WIFEXITED");
static_assert(!WIFSIGNALED(still_active), "Expected still_active to not indicate WIFSIGNALED");
static_assert(!WIFCONTINUED(still_active), "Expected still_active to not indicate WIFCONTINUED");
}
inline bool process_is_running(int code)
{
return !WIFEXITED(code) && !WIFSIGNALED(code);
}
inline int evaluate_exit_code(int code)
{
if (WIFEXITED(code))
return WEXITSTATUS(code);
else if (WIFSIGNALED(code))
return WTERMSIG(code);
else
return code;
}
#endif
#endif
/// @{
/** Helper to subsume an exit-code into an error_code if there's no actual error isn't set.
* @code {.cpp}
* process proc{ctx, "exit", {"1"}};
*
* proc.async_wait(
* asio::deferred(
* [&proc](error_code ec, int)
* {
* return asio::deferred.values(
* check_exit_code(ec, proc.native_exit_code())
* ))
* [](error_code ec)
* {
* assert(ec.value() == 10);
* assert(ec.category() == error::get_exit_code_category());
* }));
*
* @endcode
*/
inline error_code check_exit_code(
error_code &ec, native_exit_code_type native_code,
const error_category & category = error::get_exit_code_category())
{
if (!ec)
BOOST_PROCESS_V2_ASSIGN_EC(ec, native_code, category);
return ec;
}
/// @}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_EXIT_CODE_HPP

View File

@@ -0,0 +1,16 @@
//
// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.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_EXT_HPP
#define BOOST_PROCESS_V2_EXT_HPP
#include <boost/process/v2/ext/cmd.hpp>
#include <boost/process/v2/ext/cwd.hpp>
#include <boost/process/v2/ext/env.hpp>
#include <boost/process/v2/ext/exe.hpp>
#endif //BOOST_PROCESS_V2_EXT_HPP

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2022 Klemens D. Morgenstern
// Copyright (c) 2022 Samuel Venable
//
// 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_CMD_HPP
#define BOOST_PROCESS_V2_CMD_HPP
#include <string>
#include <vector>
#include <memory>
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/process_handle.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/shell.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace ext {
#if defined(BOOST_PROCESS_V2_WINDOWS)
BOOST_PROCESS_V2_DECL shell cmd(HANDLE handle, error_code & ec);
BOOST_PROCESS_V2_DECL shell cmd(HANDLE handle);
#endif
/// @{
/// Get the argument vector of another process
BOOST_PROCESS_V2_DECL shell cmd(pid_type pid, error_code & ec);
BOOST_PROCESS_V2_DECL shell cmd(pid_type pid);
#if defined(BOOST_PROCESS_V2_WINDOWS)
BOOST_PROCESS_V2_DECL shell cmd(HANDLE handle, error_code & ec);
BOOST_PROCESS_V2_DECL shell cmd(HANDLE handle);
#endif
template<typename Executor>
inline shell cmd(basic_process_handle<Executor> & handle, error_code & ec)
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
return cmd(handle.native_handle(), ec);
#else
return cmd(handle.id(), ec);
#endif
}
template<typename Executor>
inline shell cmd(basic_process_handle<Executor> & handle)
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
return cmd(handle.native_handle());
#else
return cmd(handle.id());
#endif
}
/// @}
} // namespace ext
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_CMD_HPP

View File

@@ -0,0 +1,55 @@
// Copyright (c) 2022 Klemens D. Morgenstern
// Copyright (c) 2022 Samuel Venable
//
// 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_CWD_HPP
#define BOOST_PROCESS_V2_CWD_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/process_handle.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace ext {
#if defined(BOOST_PROCESS_V2_WINDOWS)
BOOST_PROCESS_V2_DECL filesystem::path cwd(HANDLE handle, error_code & ec);
BOOST_PROCESS_V2_DECL filesystem::path cwd(HANDLE handle);
#endif
/// @{
/// Obtain the current path of another process
BOOST_PROCESS_V2_DECL filesystem::path cwd(pid_type pid, error_code & ec);
BOOST_PROCESS_V2_DECL filesystem::path cwd(pid_type pid);
template<typename Executor>
inline filesystem::path cwd(basic_process_handle<Executor> & handle, error_code & ec)
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
return cwd(handle.native_handle(), ec);
#else
return cwd(handle.id(), ec);
#endif
}
template<typename Executor>
inline filesystem::path cwd(basic_process_handle<Executor> & handle)
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
return cwd(handle.native_handle());
#else
return cwd(handle.id());
#endif
}
/// @}
} // namespace ext
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_CWD_HPP

View File

@@ -0,0 +1,119 @@
// Copyright (c) 2022 Klemens D. Morgenstern
// Copyright (c) 2022 Samuel Venable
//
// 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_DETAIL_PROC_INFO_HPP
#define BOOST_PROCESS_V2_DETAIL_PROC_INFO_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/pid.hpp>
#include <string>
#include <vector>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <iterator>
#include <algorithm>
#include <windows.h>
#include <winternl.h>
extern "C" ULONG NTAPI RtlNtStatusToDosError(NTSTATUS Status);
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
namespace ext
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
#if !defined(_MSC_VER)
#pragma pack(push, 8)
#else
#include <pshpack8.h>
#endif
/* CURDIR struct from:
https://github.com/processhacker/phnt/
CC BY 4.0 licence */
typedef struct {
UNICODE_STRING DosPath;
HANDLE Handle;
} CURDIR;
/* RTL_DRIVE_LETTER_CURDIR struct from:
https://github.com/processhacker/phnt/
CC BY 4.0 licence */
typedef struct {
USHORT Flags;
USHORT Length;
ULONG TimeStamp;
STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR;
/* RTL_USER_PROCESS_PARAMETERS struct from:
https://github.com/processhacker/phnt/
CC BY 4.0 licence */
typedef struct {
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
HANDLE ConsoleHandle;
ULONG ConsoleFlags;
HANDLE StandardInput;
HANDLE StandardOutput;
HANDLE StandardError;
CURDIR CurrentDirectory;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PVOID Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopInfo;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR CurrentDirectories[32];
ULONG_PTR EnvironmentSize;
ULONG_PTR EnvironmentVersion;
PVOID PackageDependencyData;
ULONG ProcessGroupId;
ULONG LoaderThreads;
UNICODE_STRING RedirectionDllName;
UNICODE_STRING HeapPartitionName;
ULONG_PTR DefaultThreadpoolCpuSetMasks;
ULONG DefaultThreadpoolCpuSetMaskCount;
} RTL_USER_PROCESS_PARAMETERS_EXTENDED;
#if !defined(_MSC_VER)
#pragma pack(pop)
#else
#include <poppack.h>
#endif
BOOST_PROCESS_V2_DECL std::wstring cwd_cmd_from_proc(HANDLE proc, int type, error_code & ec);
BOOST_PROCESS_V2_DECL HANDLE open_process_with_debug_privilege(boost::process::v2::pid_type pid, error_code & ec);
#endif
} // namespace ext
} // namespace detail
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_DETAIL_PROC_INFO_HPP

View File

@@ -0,0 +1,148 @@
// Copyright (c) 2022 Klemens D. Morgenstern
// Copyright (c) 2022 Samuel Venable
//
// 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_ENV_HPP
#define BOOST_PROCESS_V2_ENV_HPP
#include <string>
#include <vector>
#include <memory>
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/process_handle.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/environment.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace detail
{
namespace ext
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
using native_env_handle_type = wchar_t *;
using native_env_iterator = wchar_t *;
#else
using native_env_handle_type = char *;
using native_env_iterator = char *;
#endif
struct native_env_handle_deleter
{
BOOST_PROCESS_V2_DECL void operator()(native_env_handle_type) const;
};
BOOST_PROCESS_V2_DECL native_env_iterator next(native_env_iterator nh);
BOOST_PROCESS_V2_DECL native_env_iterator find_end(native_env_iterator nh);
BOOST_PROCESS_V2_DECL const environment::char_type * dereference(native_env_iterator iterator);
} // namespace ext
} // namespace detail
namespace ext {
/// The view of an environment
struct env_view
{
using native_handle_type = detail::ext::native_env_handle_type;
using value_type = environment::key_value_pair_view;
env_view() = default;
env_view(env_view && nt) = default;
native_handle_type native_handle() { return handle_.get(); }
struct iterator
{
using value_type = environment::key_value_pair_view;
using difference_type = int;
using reference = environment::key_value_pair_view;
using pointer = environment::key_value_pair_view;
using iterator_category = std::forward_iterator_tag;
iterator() = default;
iterator(const iterator & ) = default;
iterator(const detail::ext::native_env_iterator &native_handle) : iterator_(native_handle) {}
iterator & operator++()
{
iterator_ = detail::ext::next(iterator_);
return *this;
}
iterator operator++(int)
{
auto last = *this;
iterator_ = detail::ext::next(iterator_);
return last;
}
environment::key_value_pair_view operator*() const
{
return detail::ext::dereference(iterator_);
}
friend bool operator==(const iterator & l, const iterator & r) {return l.iterator_ == r.iterator_;}
friend bool operator!=(const iterator & l, const iterator & r) {return l.iterator_ != r.iterator_;}
private:
detail::ext::native_env_iterator iterator_;
};
iterator begin() const {return iterator(handle_.get());}
iterator end() const {return iterator(detail::ext::find_end(handle_.get()));}
private:
friend BOOST_PROCESS_V2_DECL env_view env(pid_type pid, error_code & ec);
#if defined(BOOST_PROCESS_V2_WINDOWS)
friend BOOST_PROCESS_V2_DECL env_view env(HANDLE handle, error_code & ec);
#endif
std::unique_ptr<typename remove_pointer<detail::ext::native_env_handle_type>::type,
detail::ext::native_env_handle_deleter> handle_;
};
#if defined(BOOST_PROCESS_V2_WINDOWS)
BOOST_PROCESS_V2_DECL env_view env(HANDLE handle, error_code & ec);
BOOST_PROCESS_V2_DECL env_view env(HANDLE handle);
#endif
/// @{
/// Get the environment of another process.
BOOST_PROCESS_V2_DECL env_view env(pid_type pid, error_code & ec);
BOOST_PROCESS_V2_DECL env_view env(pid_type pid);
template<typename Executor>
inline env_view env(basic_process_handle<Executor> & handle, error_code & ec)
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
return env(handle.native_handle(), ec);
#else
return env(handle.id(), ec);
#endif
}
template<typename Executor>
inline env_view env(basic_process_handle<Executor> & handle)
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
return env(handle.native_handle());
#else
return env(handle.id());
#endif
}
/// @}
} // namespace ext
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_ENV_HPP

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2022 Klemens D. Morgenstern
// Copyright (c) 2022 Samuel Venable
//
// 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_EXE_HPP
#define BOOST_PROCESS_V2_EXE_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/process_handle.hpp>
#include <boost/process/v2/pid.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace ext {
#if defined(BOOST_PROCESS_V2_WINDOWS)
BOOST_PROCESS_V2_DECL filesystem::path exe(HANDLE handle, error_code & ec);
BOOST_PROCESS_V2_DECL filesystem::path exe(HANDLE handle);
#endif
/// @{
/// Return the executable of another process by pid or handle.
BOOST_PROCESS_V2_DECL filesystem::path exe(pid_type pid, error_code & ec);
BOOST_PROCESS_V2_DECL filesystem::path exe(pid_type pid);
template<typename Executor>
filesystem::path exe(basic_process_handle<Executor> & handle, error_code & ec)
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
return exe(handle.native_handle(), ec);
#else
return exe(handle.id(), ec);
#endif
}
template<typename Executor>
filesystem::path exe(basic_process_handle<Executor> & handle)
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
return exe(handle.native_handle());
#else
return exe(handle.id());
#endif
}
///@}
} // namespace ext
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_EXE_HPP

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2022 Klemens D. Morgenstern
// Copyright (c) 2022 Samuel Venable
//
// 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_PID_HPP
#define BOOST_PROCESS_V2_PID_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <vector>
#include <memory>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
#if defined(GENERATING_DOCUMENTATION)
//An integral type representing a process id.
typedef implementation_defined pid_type;
#else
#if defined(BOOST_PROCESS_V2_WINDOWS)
typedef unsigned long pid_type;
#else
typedef int pid_type;
#endif
#endif
#if (defined(BOOST_PROCESS_V2_WINDOWS) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__sun))
constexpr static pid_type root_pid = 0;
#elif (defined(__APPLE__) && defined(__MACH__) || defined(__linux__) || defined(__ANDROID__) || defined(__OpenBSD__))
constexpr static pid_type root_pid = 1;
#endif
/// Get the process id of the current process.
BOOST_PROCESS_V2_DECL pid_type current_pid();
/// List all available pids.
BOOST_PROCESS_V2_DECL std::vector<pid_type> all_pids(error_code & ec);
/// List all available pids.
BOOST_PROCESS_V2_DECL std::vector<pid_type> all_pids();
// return parent pid of pid.
BOOST_PROCESS_V2_DECL pid_type parent_pid(pid_type pid, error_code & ec);
// return parent pid of pid.
BOOST_PROCESS_V2_DECL pid_type parent_pid(pid_type pid);
// return child pids of pid.
BOOST_PROCESS_V2_DECL std::vector<pid_type> child_pids(pid_type pid, error_code & ec);
// return child pids of pid.
BOOST_PROCESS_V2_DECL std::vector<pid_type> child_pids(pid_type pid);
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_PID_HPP

View File

@@ -0,0 +1,473 @@
// 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_POPEN_HPP
#define BOOST_PROCESS_V2_POPEN_HPP
#include <boost/process/v2/process.hpp>
#include <boost/process/v2/stdio.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/connect_pipe.hpp>
#include <asio/readable_pipe.hpp>
#include <asio/writable_pipe.hpp>
#else
#include <boost/asio/connect_pipe.hpp>
#include <boost/asio/readable_pipe.hpp>
#include <boost/asio/writable_pipe.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
/// A subprocess with automatically assigned pipes.
/** The purpose os the popen is to provide a convenient way
* to use the stdin & stdout of a process.
*
* @code {.cpp}
* popen proc(executor, find_executable("addr2line"), {argv[0]});
* asio::write(proc, asio::buffer("main\n"));
* std::string line;
* asio::read_until(proc, asio::dynamic_buffer(line), '\n');
* @endcode
*
*
* Popen can be used as a stream object in other protocols.
*/
template<typename Executor = net::any_io_executor>
struct basic_popen : basic_process<Executor>
{
/// The executor of the process
using executor_type = Executor;
/// Rebinds the popen type to another executor.
template <typename Executor1>
struct rebind_executor
{
/// The pipe type when rebound to the specified executor.
typedef basic_popen<Executor1> other;
};
/// Move construct a popen
basic_popen(basic_popen &&) = default;
/// Move assign a popen
basic_popen& operator=(basic_popen &&) = default;
/// Move construct a popen and change the executor type.
template<typename Executor1>
basic_popen(basic_popen<Executor1>&& lhs)
: basic_process<Executor>(std::move(lhs)),
stdin_(std::move(lhs.stdin_)), stdout_(std::move(lhs.stdout_))
{
}
/// Create a closed process handle
explicit basic_popen(executor_type exec) : basic_process<Executor>{std::move(exec)} {}
/// Create a closed process handle
template <typename ExecutionContext>
explicit basic_popen(ExecutionContext & context,
typename std::enable_if<
is_convertible<ExecutionContext&,
net::execution_context&>::value, void *>::type = nullptr)
: basic_process<Executor>{context}
{
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename ... Inits>
explicit basic_popen(
executor_type executor,
const filesystem::path& exe,
std::initializer_list<string_view> args,
Inits&&... inits)
: basic_process<Executor>(executor)
{
this->basic_process<Executor>::operator=(
default_process_launcher()(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
executor_type executor,
const filesystem::path& exe,
std::initializer_list<string_view> args,
Inits&&... inits)
: basic_process<Executor>(executor)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Args, typename ... Inits>
explicit basic_popen(
executor_type executor,
const filesystem::path& exe,
Args&& args, Inits&&... inits)
: basic_process<Executor>(executor)
{
this->basic_process<Executor>::operator=(
default_process_launcher()(
std::move(executor), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename Args, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
executor_type executor,
const filesystem::path& exe,
Args&& args, Inits&&... inits)
: basic_process<Executor>(executor)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
std::move(executor), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename ExecutionContext, typename ... Inits>
explicit basic_popen(
ExecutionContext & context,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
net::execution_context&>::value,
const filesystem::path&>::type exe,
std::initializer_list<string_view> args,
Inits&&... inits)
: basic_process<Executor>(context)
{
this->basic_process<Executor>::operator=(
default_process_launcher()(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_, {}}
));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename ExecutionContext, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
ExecutionContext & context,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
net::execution_context&>::value,
const filesystem::path&>::type exe,
std::initializer_list<string_view> args,
Inits&&... inits)
: basic_process<Executor>(context)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_, {}}
));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename ExecutionContext, typename Args, typename ... Inits>
explicit basic_popen(
ExecutionContext & context,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
net::execution_context&>::value,
const filesystem::path&>::type exe,
Args&& args, Inits&&... inits)
: basic_process<Executor>(context)
{
this->basic_process<Executor>::operator=(
default_process_launcher()(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
}
/// Construct a child from a property list and launch it using the default process launcher.
template<typename Launcher, typename ExecutionContext, typename Args, typename ... Inits>
explicit basic_popen(
Launcher && launcher,
ExecutionContext & context,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
net::execution_context&>::value,
const filesystem::path&>::type exe,
Args&& args, Inits&&... inits)
: basic_process<Executor>(context)
{
this->basic_process<Executor>::operator=(
std::forward<Launcher>(launcher)(
this->get_executor(), exe, args,
std::forward<Inits>(inits)...,
process_stdio{stdin_, stdout_}
));
}
/// The type used for stdin on the parent process side.
using stdin_type = net::basic_writable_pipe<Executor>;
/// The type used for stdout on the parent process side.
using stdout_type = net::basic_readable_pipe<Executor>;
/// Get the stdin pipe.
stdin_type & get_stdin() {return stdin_; }
/// Get the stdout pipe.
stdout_type & get_stdout() {return stdout_; }
/// Get the stdin pipe.
const stdin_type & get_stdin() const {return stdin_; }
/// Get the stdout pipe.
const stdout_type & get_stdout() const {return stdout_; }
/// Write some data to the pipe.
/**
* This function is used to write data to the pipe. The function call will
* block until one or more bytes of the data has been written successfully,
* or until an error occurs.
*
* @param buffers One or more data buffers to be written to the pipe.
*
* @returns The number of bytes written.
*
* @throws system_error Thrown on failure. An error code of
* boost::asio::error::eof indicates that the connection was closed by the
* subprocess.
*
* @note The write_some operation may not transmit all of the data to the
* peer. Consider using the @ref write function if you need to ensure that
* all data is written before the blocking operation completes.
*
* @par Example
* To write a single data buffer use the @ref buffer function as follows:
* @code
* pipe.write_some(boost::asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers)
{
return stdin_.write_some(buffers);
}
/// Write some data to the pipe.
/**
* This function is used to write data to the pipe. The function call will
* block until one or more bytes of the data has been written successfully,
* or until an error occurs.
*
* @param buffers One or more data buffers to be written to the pipe.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes written. Returns 0 if an error occurred.
*
* @note The write_some operation may not transmit all of the data to the
* subprocess. Consider using the @ref write function if you need to ensure that
* all data is written before the blocking operation completes.
*/
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers,
error_code& ec)
{
return stdin_.write_some(buffers, ec);
}
/// Start an asynchronous write.
/**
* This function is used to asynchronously write data to the pipe. It is an
* initiating function for an @ref asynchronous_operation, and always returns
* immediately.
*
* @param buffers One or more data buffers to be written to the pipe.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the completion handler is called.
*
* @param token The @ref completion_token that will be used to produce a
* completion handler, which will be called when the write completes.
* Potential completion tokens include @ref use_future, @ref use_awaitable,
* @ref yield_context, or a function object with the correct completion
* signature. The function signature of the completion handler must be:
* @code void handler(
* const error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes written.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the completion handler will not be invoked from within this function.
* On immediate completion, invocation of the handler will be performed in a
* manner equivalent to using boost::asio::post().
*
* @par Completion Signature
* @code void(error_code, std::size_t) @endcode
*
* @note The write operation may not transmit all of the data to the peer.
* Consider using the @ref async_write function if you need to ensure that all
* data is written before the asynchronous operation completes.
*
* @par Example
* To write a single data buffer use the @ref buffer function as follows:
* @code
* popen.async_write_some(boost::asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on writing multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename ConstBufferSequence,
BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, std::size_t))
WriteToken = net::default_completion_token_t<executor_type>>
auto async_write_some(const ConstBufferSequence& buffers,
WriteToken && token = net::default_completion_token_t<executor_type>())
-> decltype(std::declval<stdin_type&>().async_write_some(buffers, std::forward<WriteToken>(token)))
{
return stdin_.async_write_some(buffers, std::forward<WriteToken>(token));
}
/// Read some data from the pipe.
/**
* This function is used to read data from the pipe. The function call will
* block until one or more bytes of data has been read successfully, or until
* an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @returns The number of bytes read.
*
* @throws system_error Thrown on failure. An error code of
* boost::asio::error::eof indicates that the connection was closed by the
* peer.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*
* @par Example
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* basic_readable_pipe.read_some(boost::asio::buffer(data, size));
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers)
{
return stdout_.read_some(buffers);
}
/// Read some data from the pipe.
/**
* This function is used to read data from the pipe. The function call will
* block until one or more bytes of data has been read successfully, or until
* an error occurs.
*
* @param buffers One or more buffers into which the data will be read.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns The number of bytes read. Returns 0 if an error occurred.
*
* @note The read_some operation may not read all of the requested number of
* bytes. Consider using the @ref read function if you need to ensure that
* the requested amount of data is read before the blocking operation
* completes.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
error_code& ec)
{
return stdout_.read_some(buffers, ec);
}
/// Start an asynchronous read.
/**
* This function is used to asynchronously read data from the pipe. It is an
* initiating function for an @ref asynchronous_operation, and always returns
* immediately.
*
* @param buffers One or more buffers into which the data will be read.
* Although the buffers object may be copied as necessary, ownership of the
* underlying memory blocks is retained by the caller, which must guarantee
* that they remain valid until the completion handler is called.
*
* @param token The @ref completion_token that will be used to produce a
* completion handler, which will be called when the read completes.
* Potential completion tokens include @ref use_future, @ref use_awaitable,
* @ref yield_context, or a function object with the correct completion
* signature. The function signature of the completion handler must be:
* @code void handler(
* const error_code& error, // Result of operation.
* std::size_t bytes_transferred // Number of bytes read.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the completion handler will not be invoked from within this function.
* On immediate completion, invocation of the handler will be performed in a
* manner equivalent to using boost::asio::post().
*
* @par Completion Signature
* @code void(error_code, std::size_t) @endcode
*
* @note The read operation may not read all of the requested number of bytes.
* Consider using the @ref async_read function if you need to ensure that the
* requested amount of data is read before the asynchronous operation
* completes.
*
* @par Example
* To read into a single data buffer use the @ref buffer function as follows:
* @code
* basic_readable_pipe.async_read_some(
* boost::asio::buffer(data, size), handler);
* @endcode
* See the @ref buffer documentation for information on reading into multiple
* buffers in one go, and how to use it with arrays, boost::array or
* std::vector.
*/
template <typename MutableBufferSequence,
BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, std::size_t))
ReadToken = net::default_completion_token_t<executor_type>>
auto async_read_some(const MutableBufferSequence& buffers,
BOOST_ASIO_MOVE_ARG(ReadToken) token
= net::default_completion_token_t<executor_type>())
-> decltype(std::declval<stdout_type&>().async_read_some(buffers, std::forward<ReadToken>(token)))
{
return stdout_.async_read_some(buffers, std::forward<ReadToken>(token));
}
private:
stdin_type stdin_ {basic_process<Executor>::get_executor()};
stdout_type stdout_{basic_process<Executor>::get_executor()};
};
/// A popen object with the default executor.
using popen = basic_popen<>;
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_POPEN_HPP

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

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

View 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

View 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

View 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

View File

@@ -0,0 +1,403 @@
// 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)
//
//
// process.hpp
// ~~~~~~~~~~~~~~
//
#ifndef BOOST_PROCESS_V2_PROCESS_HPP
#define BOOST_PROCESS_V2_PROCESS_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/default_launcher.hpp>
#include <boost/process/v2/exit_code.hpp>
#include <boost/process/v2/pid.hpp>
#include <boost/process/v2/ext/exe.hpp>
#include <boost/process/v2/process_handle.hpp>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/any_io_executor.hpp>
#include <boost/asio/dispatch.hpp>
#include <asio/post.hpp>
#include <utility>
#else
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/core/exchange.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
/// A class managing a subprocess
/* A `basic_process` object manages a subprocess; it tracks the status and exit-code,
* and will terminate the process on destruction if `detach` was not called.
*/
template<typename Executor = net::any_io_executor>
struct basic_process
{
/// The executor of the process
using executor_type = Executor;
/// Get the executor of the process
executor_type get_executor() {return process_handle_.get_executor();}
/// The non-closing handle type
using handle_type = basic_process_handle<executor_type>;
/// Get the underlying non-closing handle
handle_type & handle() { return process_handle_; }
/// Get the underlying non-closing handle
const handle_type & handle() const { return process_handle_; }
/// Provides access to underlying operating system facilities
using native_handle_type = typename handle_type::native_handle_type;
/// Rebinds the process_handle to another executor.
template <typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process<Executor1> other;
};
/** An empty process is similar to a default constructed thread. It holds an empty
handle and is a place holder for a process that is to be launched later. */
basic_process() = default;
basic_process(const basic_process&) = delete;
basic_process& operator=(const basic_process&) = delete;
/// Move construct the process. It will be detached from `lhs`.
basic_process(basic_process&& lhs) = default;
/// Move assign a process. It will be detached from `lhs`.
basic_process& operator=(basic_process&& lhs) = default;
/// Move construct and rebind the executor.
template<typename Executor1>
basic_process(basic_process<Executor1>&& lhs)
: process_handle_(std::move(lhs.process_handle_)),
exit_status_{lhs.exit_status_}
{
}
/// Construct a child from a property list and launch it using the default launcher..
template<typename ... Inits>
explicit basic_process(
executor_type executor,
const filesystem::path& exe,
std::initializer_list<string_view> args,
Inits&&... inits)
: basic_process(default_process_launcher()(std::move(executor), exe, args, std::forward<Inits>(inits)...))
{
}
/// Construct a child from a property list and launch it using the default launcher..
template<typename Args, typename ... Inits>
explicit basic_process(
executor_type executor,
const filesystem::path& exe,
Args&& args, Inits&&... inits)
: basic_process(default_process_launcher()(std::move(executor), exe,
std::forward<Args>(args), std::forward<Inits>(inits)...))
{
}
/// Construct a child from a property list and launch it using the default launcher..
template<typename ExecutionContext, typename ... Inits>
explicit basic_process(
ExecutionContext & context,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
net::execution_context&>::value,
const filesystem::path&>::type exe,
std::initializer_list<string_view> args,
Inits&&... inits)
: basic_process(default_process_launcher()(executor_type(context.get_executor()),
exe, args, std::forward<Inits>(inits)...))
{
}
/// Construct a child from a property list and launch it using the default launcher.
template<typename ExecutionContext, typename Args, typename ... Inits>
explicit basic_process(
ExecutionContext & context,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
net::execution_context&>::value,
const filesystem::path&>::type exe,
Args&& args, Inits&&... inits)
: basic_process(default_process_launcher()(executor_type(context.get_executor()),
exe, std::forward<Args>(args), std::forward<Inits>(inits)...))
{
}
/// Attach to an existing process
explicit basic_process(executor_type exec, pid_type pid) : process_handle_(std::move(exec), pid) {}
/// Attach to an existing process and the internal handle
explicit basic_process(executor_type exec, pid_type pid, native_handle_type native_handle)
: process_handle_(std::move(exec), pid, native_handle) {}
/// Create an invalid handle
explicit basic_process(executor_type exec) : process_handle_{std::move(exec)} {}
/// Attach to an existing process
template <typename ExecutionContext>
explicit basic_process(ExecutionContext & context, pid_type pid,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
net::execution_context&>::value, void *>::type = nullptr)
: process_handle_(context, pid) {}
/// Attach to an existing process and the internal handle
template <typename ExecutionContext>
explicit basic_process(ExecutionContext & context, pid_type pid, native_handle_type native_handle,
typename std::enable_if<
std::is_convertible<ExecutionContext&,
net::execution_context&>::value, void *>::type = nullptr)
: process_handle_(context.get_executor(), pid, native_handle) {}
/// Create an invalid handle
template <typename ExecutionContext>
explicit basic_process(ExecutionContext & context,
typename std::enable_if<
is_convertible<ExecutionContext&,
net::execution_context&>::value, void *>::type = nullptr)
: process_handle_(context.get_executor()) {}
/// Destruct the handle and terminate the process if it wasn't detached.
~basic_process()
{
process_handle_.terminate_if_running();
}
/// Sends the process a signal to ask for an interrupt, which the process may interpret as a shutdown.
/** Maybe be ignored by the subprocess. */
void interrupt()
{
error_code ec;
interrupt(ec);
if (ec)
throw system_error(ec, "interrupt failed");
}
/// Throwing @overload void interrupt()
void interrupt(error_code & ec)
{
process_handle_.interrupt(ec);
}
/// Throwing @overload void request_exit(error_code & ec)
void request_exit()
{
error_code ec;
request_exit(ec);
if (ec)
throw system_error(ec, "request_exit failed");
}
/// Sends the process a signal to ask for a graceful shutdown. Maybe be ignored by the subprocess.
void request_exit(error_code & ec)
{
process_handle_.request_exit(ec);
}
/// Send the process a signal requesting it to stop. This may rely on undocumented functions.
void suspend(error_code &ec)
{
process_handle_.suspend(ec);
}
/// Send the process a signal requesting it to stop. This may rely on undocumented functions.
void suspend()
{
error_code ec;
suspend(ec);
if (ec)
detail::throw_error(ec, "suspend");
}
/// Send the process a signal requesting it to resume. This may rely on undocumented functions.
void resume(error_code &ec)
{
process_handle_.resume(ec);
}
/// Send the process a signal requesting it to resume. This may rely on undocumented functions.
void resume()
{
error_code ec;
resume(ec);
if (ec)
detail::throw_error(ec, "resume");
}
/// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
void terminate()
{
error_code ec;
terminate(ec);
if (ec)
detail::throw_error(ec, "terminate failed");
}
/// Unconditionally terminates the process and stores the exit code in exit_status.
void terminate(error_code & ec)
{
process_handle_.terminate(exit_status_, ec);
}
/// Throwing @overload wait(error_code & ec)
int wait()
{
error_code ec;
if (running(ec))
process_handle_.wait(exit_status_, ec);
if (ec)
detail::throw_error(ec, "wait failed");
return exit_code();
}
/// Waits for the process to exit, store the exit code internally and return it.
int wait(error_code & ec)
{
if (running(ec))
process_handle_.wait(exit_status_, ec);
return exit_code();
}
/// Detach the process.
handle_type detach()
{
#if defined(BOOST_PROCESS_V2_STANDALONE)
return std::exchange(process_handle_, get_executor());
#else
return boost::exchange(process_handle_, get_executor());
#endif
}
/// Get the native
native_handle_type native_handle() {return process_handle_.native_handle(); }
/// Return the evaluated exit_code.
int exit_code() const
{
return evaluate_exit_code(exit_status_);
}
/// Get the id of the process;
pid_type id() const {return process_handle_.id();}
/// The native handle of the process.
/** This might be undefined on posix systems that only support signals */
native_exit_code_type native_exit_code() const
{
return exit_status_;
}
/// Checks if the current process is running.
/** If it has already completed the exit code will be stored internally
* and can be obtained by calling `exit_code.
*/
bool running()
{
if (!process_is_running(exit_status_))
return false;
error_code ec;
native_exit_code_type exit_code{};
auto r = process_handle_.running(exit_code, ec);
if (!ec && !r)
exit_status_ = exit_code;
else
detail::throw_error(ec, "running failed");
return r;
}
/// Throwing @overload bool running(error_code & ec)
bool running(error_code & ec) noexcept
{
if (!process_is_running(exit_status_))
return false;
native_exit_code_type exit_code{};
auto r = process_handle_.running(exit_code, ec);
if (!ec && !r)
exit_status_ = exit_code;
return r;
}
/// Check if the process is referring to an existing process.
/** Note that this might be a process that already exited.*/
bool is_open() const { return process_handle_.is_open(); }
private:
template<typename Executor1>
friend struct basic_process;
basic_process_handle<Executor> process_handle_;
native_exit_code_type exit_status_{detail::still_active};
struct async_wait_op_
{
basic_process_handle<Executor> & handle;
native_exit_code_type & res;
template<typename Self>
void operator()(Self && self)
{
if (!process_is_running(res))
{
struct completer
{
int code;
typename std::decay<Self>::type self;
void operator()()
{
self.complete(error_code{}, evaluate_exit_code(code));
}
};
net::dispatch(
net::get_associated_immediate_executor(handle, handle.get_executor()),
completer{static_cast<int>(res), std::move(self)});
}
else
handle.async_wait(res, std::move(self));
}
template<typename Self>
void operator()(Self && self, error_code ec)
{
if (!ec && process_is_running(res))
handle.async_wait(res, std::move(self));
else
{
std::move(self).complete(ec, evaluate_exit_code(res));
}
}
};
public:
/// Asynchronously wait for the process to exit and deliver the native exit-code in the completion handler.
template <BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, int))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(WaitHandler && handler = net::default_completion_token_t<executor_type>())
-> decltype(net::async_compose<WaitHandler, void (error_code, int)>(
async_wait_op_{process_handle_, exit_status_}, handler, process_handle_))
{
return net::async_compose<WaitHandler, void (error_code, int)>(
async_wait_op_{process_handle_, exit_status_}, handler, process_handle_);
}
};
/// Process with the default executor.
typedef basic_process<> process;
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_PROCESS_HPP

View File

@@ -0,0 +1,158 @@
// 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_PROCESS_HANDLE_HPP
#define BOOST_PROCESS_V2_PROCESS_HANDLE_HPP
#include <boost/process/v2/detail/config.hpp>
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <boost/process/v2/detail/process_handle_windows.hpp>
#else
#if defined(BOOST_PROCESS_V2_PIDFD_OPEN)
#include <boost/process/v2/detail/process_handle_fd.hpp>
#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPEFORK)
#include <boost/process/v2/detail/process_handle_fd_or_signal.hpp>
#else
// with asio support we could use EVFILT_PROC:NOTE_EXIT as well.
#include <boost/process/v2/detail/process_handle_signal.hpp>
#endif
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
#if defined(GENERATING_DOCUMENTATION)
/** A process handle is an unmanaged version of a process.
* This means it does not terminate the process on destruction and
* will not keep track of the exit-code.
*
* Note that the exit code might be discovered early, during a call to `running`.
* Thus it can only be discovered that process has exited already.
*/
template<typename Executor = net::any_io_executor>
struct basic_process_handle
{
/// The native handle of the process.
/** This might be undefined on posix systems that only support signals */
using native_handle_type = implementation_defined;
/// The executor_type of the process_handle
using executor_type = Executor;
/// Getter for the executor
executor_type get_executor();
/// Rebinds the process_handle to another executor.
template<typename Executor1>
struct rebind_executor
{
/// The socket type when rebound to the specified executor.
typedef basic_process_handle<Executor1> other;
};
/// Construct a basic_process_handle from an execution_context.
/**
* @tparam ExecutionContext The context must fulfill the asio::execution_context requirements
*/
template<typename ExecutionContext>
basic_process_handle(ExecutionContext &context);
/// Construct an empty process_handle from an executor.
basic_process_handle(executor_type executor);
/// Construct an empty process_handle from an executor and bind it to a pid.
/** On NON-linux posix systems this call is not able to obtain a file-descriptor and will thus
* rely on signals.
*/
basic_process_handle(executor_type executor, pid_type pid);
/// Construct an empty process_handle from an executor and bind it to a pid and the native-handle
/** On some non-linux posix systems this overload is not present.
*/
basic_process_handle(executor_type executor, pid_type pid, native_handle_type process_handle);
/// Move construct and rebind the executor.
template<typename Executor1>
basic_process_handle(basic_process_handle<Executor1> &&handle);
/// Get the id of the process
pid_type id() const
{ return pid_; }
/// Terminate the process if it's still running and ignore the result
void terminate_if_running(error_code &);
/// Throwing @overload void terminate_if_running(error_code & ec;
void terminate_if_running();
/// wait for the process to exit and store the exit code in exit_status.
void wait(native_exit_code_type &exit_status, error_code &ec);
/// Throwing @overload wait(native_exit_code_type &exit_code, error_code & ec)
void wait(native_exit_code_type &exit_status);
/// Sends the process a signal to ask for an interrupt, which the process may interpret as a shutdown.
/** Maybe be ignored by the subprocess. */
void interrupt(error_code &ec);
/// Throwing @overload void interrupt()
void interrupt();
/// Sends the process a signal to ask for a graceful shutdown. Maybe be ignored by the subprocess.
void request_exit(error_code &ec);
/// Throwing @overload void request_exit(error_code & ec)
void request_exit()
/// Unconditionally terminates the process and stores the exit code in exit_status.
void terminate(native_exit_code_type &exit_status, error_code &ec);
/// Throwing @overload void terminate(native_exit_code_type &exit_code, error_code & ec)
void terminate(native_exit_code_type &exit_status);
/// Checks if the current process is running.
/**If it has already completed, it assigns the exit code to `exit_code`.
*/
bool running(native_exit_code_type &exit_code, error_code &ec);
/// Throwing @overload bool running(native_exit_code_type &exit_code, error_code & ec)
bool running(native_exit_code_type &exit_code);
/// Check if the process handle is referring to an existing process.
bool is_open() const;
/// Asynchronously wait for the process to exit and deliver the native exit-code in the completion handler.
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, native_exit_code_type))
WaitHandler = net::default_completion_token_t<executor_type>>
auto async_wait(WaitHandler &&handler = net::default_completion_token_t<executor_type>());
};
#else
#if defined(BOOST_PROCESS_V2_WINDOWS)
template<typename Executor = net::any_io_executor>
using basic_process_handle = detail::basic_process_handle_win<Executor>;
#else
#if defined(BOOST_PROCESS_V2_PIDFD_OPEN)
template<typename Executor = net::any_io_executor>
using basic_process_handle = detail::basic_process_handle_fd<Executor>;
#elif defined(BOOST_PROCESS_V2_PDFORK) || defined(BOOST_PROCESS_V2_PIPEFORK)
template<typename Executor = net::any_io_executor>
using basic_process_handle = detail::basic_process_handle_fd_or_signal<Executor>;
#else
template<typename Executor = net::any_io_executor>
using basic_process_handle = detail::basic_process_handle_signal<Executor>;
#endif
#endif
/// Process handle with the default executor.
using process_handle = basic_process_handle<>;
#endif
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_PROCESS_HANDLE_HPP

View File

@@ -0,0 +1,143 @@
// 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_SHELL_HPP
#define BOOST_PROCESS_V2_SHELL_HPP
#include <boost/core/exchange.hpp>
#include <boost/process/v2/cstring_ref.hpp>
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/utf8.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/environment.hpp>
#include <memory>
#include <string>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
/// Error category used by the shell parser.
extern BOOST_PROCESS_V2_DECL const error_category& get_shell_category();
static const error_category& shell_category = get_shell_category();
/// Utility to parse commands
/** This utility class parses command lines into tokens
* and allows users to executed based on textual inputs.
*
* In v1, this was possible directly when starting a process,
* but has been removed based on the security risks associated with this.
*
* By making the shell parsing explicitly, it encourages
* a user to run a sanity check on the executable before launching it.
*
* @par Example
* @code {.cpp}
* asio::io_context ctx;
*
* auto cmd = shell("my-app --help");
* auto exe = cmd.exe();
* check_if_malicious(exe);
*
* process proc{ctx, exe, cmd.args()};
*
* @endcode
*
*
*/
struct shell
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
using char_type = wchar_t;
using args_type = const wchar_t *;
#else
using char_type = char;
using args_type = const char **;
#endif
shell() = default;
template<typename Char, typename Traits>
shell(basic_string_view<Char, Traits> input)
: buffer_(detail::conv_string<char_type>(input.data(), input.size()))
{
parse_();
}
shell(basic_cstring_ref<char_type> input) : input_(input) {parse_();}
shell(basic_string_view<
typename std::conditional<
std::is_same<char_type, char>::value,
wchar_t, char>::type> input) : buffer_(detail::conv_string<char_type>(input.data(), input.size()))
{
parse_();
}
shell(const shell &) = delete;
shell& operator=(const shell &) = delete;
shell(shell && lhs) noexcept
: buffer_(std::move(lhs.buffer_)),
input_(std::move(lhs.input_)),
argc_(boost::exchange(lhs.argc_, 0)),
argv_(boost::exchange(lhs.argv_, nullptr))
#if defined(BOOST_PROCESS_V2_POSIX)
, free_argv_(boost::exchange(lhs.free_argv_, nullptr))
#endif
{
}
shell& operator=(shell && lhs) noexcept
{
shell tmp(std::move(*this));
buffer_ = std::move(lhs.buffer_);
input_ = std::move(lhs.input_);
argc_ = boost::exchange(lhs.argc_, 0);
argv_ = boost::exchange(lhs.argv_, nullptr);
#if defined(BOOST_PROCESS_V2_POSIX)
free_argv_ = boost::exchange(lhs.free_argv_, nullptr);
#endif
return *this;
}
// the length of the parsed shell, including the executable
int argc() const { return argc_; }
char_type** argv() const { return argv_; }
char_type** begin() const {return argv();}
char_type** end() const {return argv() + argc();}
bool empty() const {return argc() == 0;}
std::size_t size() const {return static_cast<std::size_t>(argc()); }
/// Native representation of the arguments to be used - excluding the executable
BOOST_PROCESS_V2_DECL args_type args() const;
template<typename Environment = environment::current_view>
filesystem::path exe(Environment && env = environment::current()) const
{
if (argc() == 0)
return "";
else
return environment::find_executable(0[argv()], std::forward<Environment>(env));
}
BOOST_PROCESS_V2_DECL ~shell();
private:
friend struct make_cmd_shell_;
BOOST_PROCESS_V2_DECL void parse_();
// storage in case we need a conversion
std::basic_string<char_type> buffer_;
basic_cstring_ref<char_type> input_{buffer_};
// impl details
int argc_ = 0;
char_type ** argv_ = nullptr;
#if defined(BOOST_PROCESS_V2_POSIX)
void(*free_argv_)(int, char **);
#endif
};
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_ERROR_HPP

View File

@@ -0,0 +1,51 @@
//
// process/start_dir.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_START_DIR_HPP
#define BOOST_PROCESS_v2_START_DIR_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
/// Initializer for the starting directory of a subprocess to be launched.
struct process_start_dir
{
filesystem::path start_dir;
process_start_dir(filesystem::path start_dir) : start_dir(std::move(start_dir))
{
}
#if defined(BOOST_PROCESS_V2_WINDOWS)
error_code on_setup(windows::default_launcher & launcher,
const filesystem::path &, const std::wstring &)
{
launcher.current_directory = start_dir;
return error_code {};
};
#else
error_code on_exec_setup(posix::default_launcher & /*launcher*/,
const filesystem::path &, const char * const *)
{
if (::chdir(start_dir.c_str()) == -1)
return detail::get_last_error();
else
return error_code ();
}
#endif
};
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_v2_START_DIR_HPP

View File

@@ -0,0 +1,367 @@
//
// 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/detail/last_error.hpp>
#include <boost/process/v2/default_launcher.hpp>
#include <cstddef>
#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
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <io.h>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
template<typename T>
struct is_readable_pipe : std::false_type
{
};
template<typename Executor>
struct is_readable_pipe<asio::basic_readable_pipe<Executor>> : std::true_type
{
};
template<typename T>
struct is_writable_pipe : std::false_type
{
};
template<typename Executor>
struct is_writable_pipe<asio::basic_writable_pipe<Executor>> : std::true_type
{
};
namespace detail
{
#if defined(BOOST_PROCESS_V2_WINDOWS)
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 Target>
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(Target), false};
static DWORD get_flags(HANDLE h)
{
DWORD res;
if (!::GetHandleInformation(h, &res))
{
error_code ec;
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
throw system_error(ec, "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(reinterpret_cast<HANDLE>(::_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")) {}
template<typename T, typename = typename std::enable_if<std::is_same<T, filesystem::path>::value>::type>
process_io_binding(const T & pth)
: h(::CreateFileW(
pth.c_str(),
Target == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr
), true)
{
}
template<typename ReadablePipe>
process_io_binding(ReadablePipe & pipe,
typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STD_INPUT_HANDLE>::type * = nullptr)
{
net::detail::native_pipe_handle p[2];
error_code ec;
net::detail::create_pipe(p, ec);
if (ec)
detail::throw_error(ec, "create_pipe");
h = std::unique_ptr<void, handle_closer>{p[1], true};
pipe.assign(p[0]);
}
template<typename WritablePipe>
process_io_binding(WritablePipe & pipe,
typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STD_INPUT_HANDLE>::type * = nullptr)
{
net::detail::native_pipe_handle p[2];
error_code ec;
net::detail::create_pipe(p, ec);
if (ec)
detail::throw_error(ec, "create_pipe");
h = std::unique_ptr<void, handle_closer>{p[0], true};
pipe.assign(p[1]);
}
};
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;
process_io_binding(const process_io_binding &) = delete;
process_io_binding & operator=(const process_io_binding &) = delete;
process_io_binding(process_io_binding && other) noexcept
: fd(other.fd), fd_needs_closing(other.fd_needs_closing), ec(other.ec)
{
other.fd = target;
other.fd_needs_closing = false;
other.ec = {};
}
process_io_binding & operator=(process_io_binding && other) noexcept
{
if (fd_needs_closing)
::close(fd);
fd = other.fd;
fd_needs_closing = other.fd_needs_closing;
ec = other.ec;
other.fd = target;
other.fd_needs_closing = false;
other.ec = {};
return *this;
}
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(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 ReadablePipe>
process_io_binding(ReadablePipe & readable_pipe,
typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STDIN_FILENO>::type * = nullptr)
{
net::detail::native_pipe_handle p[2];
net::detail::create_pipe(p, ec);
if (ec)
detail::throw_error(ec, "create_pipe");
fd = p[1];
if (::fcntl(p[0], F_SETFD, FD_CLOEXEC) == -1)
{
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
return ;
}
fd_needs_closing = true;
readable_pipe.assign(p[0], ec);
}
template<typename WritablePipe>
process_io_binding(WritablePipe & writable_pipe,
typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STDIN_FILENO>::type * = nullptr)
{
net::detail::native_pipe_handle p[2];
error_code ec;
net::detail::create_pipe(p, ec);
if (ec)
detail::throw_error(ec, "create_pipe");
fd = p[0];
if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
{
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
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 &,
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{.err=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 compatible with the above. E.g. a asio::ip::tcp::socket
* - an asio::basic_writeable_pipe for stdin or asio::basic_readable_pipe for stderr/stdout.
*
*
*/
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.inherited_handles.reserve(launcher.inherited_handles.size() + 3);
launcher.inherited_handles.push_back(launcher.startup_info.StartupInfo.hStdInput);
launcher.inherited_handles.push_back(launcher.startup_info.StartupInfo.hStdOutput);
launcher.inherited_handles.push_back(launcher.startup_info.StartupInfo.hStdError);
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());
return error_code {};
};
#endif
};
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_STDIO_HPP

View File

@@ -0,0 +1,144 @@
//
// boost/process/v2/windows/default_launcher.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_WINDOWS_AS_USER_LAUNCHER_HPP
#define BOOST_PROCESS_V2_WINDOWS_AS_USER_LAUNCHER_HPP
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/windows/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace windows
{
/// A windows launcher using CreateProcessAsUser instead of CreateProcess
struct as_user_launcher : default_launcher
{
/// The token to be used in CreateProcessAsUser.
HANDLE token;
as_user_launcher(HANDLE token = INVALID_HANDLE_VALUE) : token(token) {}
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, "as_user_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)
detail::throw_error(ec, "as_user_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 command_line = this->build_command_line(executable, args);
ec = detail::on_setup(*this, executable, command_line, inits...);
if (ec)
{
detail::on_error(*this, executable, command_line, ec, inits...);
return basic_process<Executor>(exec);
}
if (!inherited_handles.empty())
{
set_handle_list(ec);
if (ec)
return basic_process<Executor>(exec);
}
auto ok = ::CreateProcessAsUserW(
token,
executable.empty() ? nullptr : executable.c_str(),
command_line.empty() ? nullptr : &command_line.front(),
process_attributes,
thread_attributes,
inherited_handles.empty() ? FALSE : TRUE,
creation_flags,
environment,
current_directory.empty() ? nullptr : current_directory.c_str(),
&startup_info.StartupInfo,
&process_information);
if (ok == 0)
{
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
detail::on_error(*this, executable, command_line, ec, inits...);
if (process_information.hProcess != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hProcess);
if (process_information.hThread != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hThread);
return basic_process<Executor>(exec);
} else
{
detail::on_success(*this, executable, command_line, inits...);
if (process_information.hThread != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hThread);
return basic_process<Executor>(exec,
this->process_information.dwProcessId,
this->process_information.hProcess);
}
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_WINDOWS_AS_USER_LAUNCHER_HPP

View File

@@ -0,0 +1,50 @@
//
// boost/process/v2/windows/default_launcher.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_WINDOWS_CREATION_FLAGS_HPP
#define BOOST_PROCESS_V2_WINDOWS_CREATION_FLAGS_HPP
#include <boost/process/v2/windows/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace windows
{
/// An initializer to add to the dwFlags in the startup-info
/**
* @tparam Flags The flags to be set.
*/
template<DWORD Flags>
struct process_creation_flags
{
constexpr process_creation_flags () {}
error_code on_setup(windows::default_launcher & launcher,
const filesystem::path &,
const std::wstring &) const
{
launcher.creation_flags |= Flags;
return error_code {};
};
};
/// A flag to create a new process group. Necessary to allow interrupts for the subprocess.
constexpr static process_creation_flags<CREATE_NEW_PROCESS_GROUP> create_new_process_group;
constexpr static process_creation_flags<CREATE_BREAKAWAY_FROM_JOB> create_breakaway_from_job;
constexpr static process_creation_flags<CREATE_NEW_CONSOLE> create_new_console;
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_WINDOWS_CREATION_FLAGS_HPP

View File

@@ -0,0 +1,451 @@
//
// boost/process/v2/windows/default_launcher.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_WINDOWS_DEFAULT_LAUNCHER_HPP
#define BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
#include <boost/process/v2/cstring_ref.hpp>
#include <boost/process/v2/detail/config.hpp>
#include <boost/process/v2/detail/last_error.hpp>
#include <boost/process/v2/detail/throw_error.hpp>
#include <boost/process/v2/detail/utf8.hpp>
#include <boost/process/v2/error.hpp>
#include <numeric>
#include <memory>
#include <type_traits>
#include <windows.h>
#if defined(BOOST_PROCESS_V2_STANDALONE)
#include <asio/execution/executor.hpp>
#include <asio/is_executor.hpp>
#include <asio/execution_context.hpp>
#else
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/is_executor.hpp>
#include <boost/asio/execution_context.hpp>
#endif
BOOST_PROCESS_V2_BEGIN_NAMESPACE
template<typename Executor>
struct basic_process;
namespace detail
{
struct base {};
struct derived : base {};
template<typename Launcher, typename Init>
inline error_code invoke_on_setup(Launcher & /*launcher*/, const filesystem::path &executable, std::wstring &cmd_line,
Init && /*init*/, base && )
{
return error_code{};
}
template<typename Launcher, typename Init>
inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
Init && init, derived && )
-> decltype(init.on_setup(launcher, executable, cmd_line))
{
return init.on_setup(launcher, executable, cmd_line);
}
template<typename Launcher, typename Init>
inline std::false_type probe_on_setup(
Launcher & launcher, Init && init, base && );
template<typename Launcher, typename Init>
inline auto probe_on_setup(Launcher & launcher, Init && init, derived && )
-> std::is_same<error_code, decltype(init.on_setup(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
template<typename Launcher, typename Init>
using has_on_setup = decltype(probe_on_setup(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
template<typename Launcher>
inline error_code on_setup(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/)
{
return error_code{};
}
template<typename Launcher, typename Init1, typename ... Inits>
inline error_code on_setup(Launcher & launcher, const filesystem::path &executable, std::wstring &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*/, std::wstring &/*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, std::wstring &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, typename Init>
inline std::false_type probe_on_error(
Launcher & launcher, Init && init, base && );
template<typename Launcher, typename Init>
inline auto probe_on_error(Launcher & launcher, Init && init, derived && )
-> std::is_same<error_code, decltype(init.on_error(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>(), std::declval<error_code&>()))>;
template<typename Launcher, typename Init>
using has_on_error = decltype(probe_on_error(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
template<typename Launcher>
inline void on_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/,
const error_code & /*ec*/)
{
}
template<typename Launcher, typename Init1, typename ... Inits>
inline void on_error(Launcher & launcher, const filesystem::path &executable, std::wstring &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*/, std::wstring &/*cmd_line*/,
Init && /*init*/, base && )
{
}
template<typename Launcher, typename Init>
inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &cmd_line,
Init && init, derived && )
-> decltype(init.on_success(launcher, executable, cmd_line))
{
init.on_success(launcher, executable, cmd_line);
}
template<typename Launcher, typename Init>
inline std::false_type probe_on_success(
Launcher & launcher, Init && init, base && );
template<typename Launcher, typename Init>
inline auto probe_on_success(Launcher & launcher, Init && init, derived && )
-> std::is_same<error_code, decltype(init.on_success(launcher, std::declval<const filesystem::path &>(), std::declval<std::wstring &>()))>;
template<typename Launcher, typename Init>
using has_on_success = decltype(probe_on_success(std::declval<Launcher&>(), std::declval<Init>(), derived{}));
template<typename Launcher>
inline void on_success(Launcher & /*launcher*/, const filesystem::path &/*executable*/, std::wstring &/*cmd_line*/)
{
}
template<typename Launcher, typename Init1, typename ... Inits>
inline void on_success(Launcher & launcher, const filesystem::path &executable, std::wstring &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>
struct is_initializer : std::integral_constant<bool,
has_on_setup<Launcher, Init>::value ||
has_on_error<Launcher, Init>::value ||
has_on_success<Launcher, Init>::value>
{
};
template<typename Launcher, typename ... Inits>
struct all_are_initializers;
template<typename Launcher>
struct all_are_initializers<Launcher> : std::true_type {};
template<typename Launcher, typename Init>
struct all_are_initializers<Launcher, Init> : is_initializer<Launcher, Init> {};
template<typename Launcher, typename Init, typename ... Tail>
struct all_are_initializers<Launcher, Init, Tail...>
: std::integral_constant<bool, is_initializer<Launcher, Init>::value && all_are_initializers<Launcher, Tail...>::value>
{
};
}
template<typename Executor>
struct basic_process;
namespace windows
{
/// The default launcher for processes on windows.
struct default_launcher
{
//// The process_attributes passed to CreateProcess
SECURITY_ATTRIBUTES * process_attributes = nullptr;
//// The thread_attributes passed to CreateProcess
SECURITY_ATTRIBUTES * thread_attributes = nullptr;
/// The inhreited_handles option. bInheritHandles will be true if not empty..
std::vector<HANDLE> inherited_handles;
/// The creation flags of the process. Initializers may add to them; extended startupinfo is assumed.
DWORD creation_flags{EXTENDED_STARTUPINFO_PRESENT};
/// A pointer to the subprocess environment.
void * environment = nullptr;
/// The startup director. An empty path will get ignored.
filesystem::path current_directory{};
/// The full startup info passed to CreateProcess
STARTUPINFOEXW startup_info{{sizeof(STARTUPINFOEXW), nullptr, nullptr, nullptr,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE},
nullptr};
/// Allow batch files to be executed, which might pose a security threat.
bool allow_batch_files = false;
/// The process_information that gets assigned after a call to CreateProcess
PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
template<typename Executor, typename ... Inits>
using enable_init = typename std::enable_if<
detail::all_are_initializers<default_launcher, Inits...>::value,
basic_process<Executor>>::type;
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 ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
{
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 ) -> enable_init<typename ExecutionContext::executor_type, Inits...>
{
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 ) -> enable_init<Executor, Inits...>
{
error_code ec;
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
if (ec)
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 ) -> enable_init<Executor, Inits...>
{
if (!allow_batch_files && ((executable.extension() == ".bat") || (executable.extension() == ".cmd")))
{
BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_ACCESS_DENIED, system_category());
return basic_process<Executor>(exec);
}
auto command_line = this->build_command_line(executable, std::forward<Args>(args));
ec = detail::on_setup(*this, executable, command_line, inits...);
if (ec)
{
detail::on_error(*this, executable, command_line, ec, inits...);
return basic_process<Executor>(exec);
}
if (!inherited_handles.empty())
{
set_handle_list(ec);
if (ec)
return basic_process<Executor>(exec);
}
auto ok = ::CreateProcessW(
executable.empty() ? nullptr : executable.c_str(),
command_line.empty() ? nullptr : &command_line.front(),
process_attributes,
thread_attributes,
inherited_handles.empty() ? FALSE : TRUE,
creation_flags,
environment,
current_directory.empty() ? nullptr : current_directory.c_str(),
&startup_info.StartupInfo,
&process_information);
if (ok == 0)
{
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
detail::on_error(*this, executable, command_line, ec, inits...);
if (process_information.hProcess != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hProcess);
if (process_information.hThread != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hThread);
return basic_process<Executor>(exec);
}
else
{
detail::on_success(*this, executable, command_line, inits...);
if (process_information.hThread != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hThread);
return basic_process<Executor>(exec,
this->process_information.dwProcessId,
this->process_information.hProcess);
}
}
BOOST_PROCESS_V2_DECL static
std::size_t escaped_argv_length(basic_string_view<wchar_t> ws);
BOOST_PROCESS_V2_DECL static
std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
basic_string_view<wchar_t> ws);
template<typename Argv>
static std::wstring build_command_line_impl(
const filesystem::path & pt,
const Argv & argv,
basic_string_view<wchar_t> args)
{
std::size_t req_size = std::accumulate(
std::begin(argv), std::end(argv), escaped_argv_length(pt.native()),
[](std::size_t sz, basic_string_view<wchar_t> arg) -> std::size_t
{
return sz + 1u + escaped_argv_length(arg);
});
std::wstring res;
res.resize(req_size, L' ');
wchar_t * itr = &res.front();
itr += escape_argv_string(itr, res.size(), pt.native());
for (const auto & a : argv)
{
itr++;
itr += escape_argv_string(itr, std::distance(itr, &res.back() + 1), a);
}
return res;
}
template<typename Argv>
static std::wstring build_command_line_impl(
const filesystem::path & pt,
const Argv & argv,
basic_string_view<char> args)
{
std::vector<std::wstring> argw;
argw.resize(std::distance(std::begin(argv), std::end(argv)));
std::transform(std::begin(argv), std::end(argv), argw.begin(),
[](basic_string_view <char> arg)
{
return detail::conv_string<wchar_t>(arg.data(), arg.size());
});
return build_command_line_impl(pt, argw, L"");
}
template<typename Args,
typename Char = decltype(*std::begin(std::declval<Args>()))>
static std::wstring build_command_line(const filesystem::path & pt, const Args & args)
{
if (std::begin(args) == std::end(args))
{
std::wstring buffer;
buffer.resize(escaped_argv_length(pt.native()));
if (!buffer.empty())
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
return buffer;
}
return build_command_line_impl(pt, args, *std::begin(args));
}
static std::wstring build_command_line(const filesystem::path & pt, const wchar_t * args)
{
return args;
}
struct lpproc_thread_closer
{
void operator()(::LPPROC_THREAD_ATTRIBUTE_LIST l)
{
::DeleteProcThreadAttributeList(l);
::HeapFree(GetProcessHeap(), 0, l);
}
};
std::unique_ptr<std::remove_pointer<LPPROC_THREAD_ATTRIBUTE_LIST>::type, lpproc_thread_closer> proc_attribute_list_storage;
BOOST_PROCESS_V2_DECL LPPROC_THREAD_ATTRIBUTE_LIST get_thread_attribute_list(error_code & ec);
BOOST_PROCESS_V2_DECL void set_handle_list(error_code & ec);
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP

View File

@@ -0,0 +1,53 @@
//
// boost/process/v2/windows/default_launcher.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_WINDOWS_SHOW_WINDOW_HPP
#define BOOST_PROCESS_V2_WINDOWS_SHOW_WINDOW_HPP
#include <boost/process/v2/windows/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace windows
{
/// A templated initializer to add wShowWindow flags.
template<DWORD Flags>
struct process_show_window
{
constexpr process_show_window() {}
error_code on_setup(windows::default_launcher & launcher,
const filesystem::path &,
const std::wstring &) const
{
launcher.startup_info.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
launcher.startup_info.StartupInfo.wShowWindow |= Flags;
return error_code {};
};
};
///Hides the window and activates another window.
constexpr static process_show_window<SW_HIDE > show_window_hide;
///Activates the window and displays it as a maximized window.
constexpr static process_show_window<SW_SHOWMAXIMIZED > show_window_maximized;
///Activates the window and displays it as a minimized window.
constexpr static process_show_window<SW_SHOWMINIMIZED > show_window_minimized;
///Displays the window as a minimized window. This value is similar to `minimized`, except the window is not activated.
constexpr static process_show_window<SW_SHOWMINNOACTIVE> show_window_minimized_not_active;
///Displays a window in its most recent size and position. This value is similar to show_normal`, except that the window is not activated.
constexpr static process_show_window<SW_SHOWNOACTIVATE > show_window_not_active;
///Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when displaying the window for the first time.
constexpr static process_show_window<SW_SHOWNORMAL > show_window_normal;
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_WINDOWS_SHOW_WINDOW_HPP

View File

@@ -0,0 +1,140 @@
//
// boost/process/v2/windows/default_launcher.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_WINDOWS_WITH_LOGON_LAUNCHER_HPP
#define BOOST_PROCESS_V2_WINDOWS_WITH_LOGON_LAUNCHER_HPP
#include <boost/process/v2/windows/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace windows
{
/// A windows launcher using CreateProcessWithLogon instead of CreateProcess
struct with_logon_launcher : default_launcher
{
std::wstring username, password, domain;
DWORD logon_flags{0u};
with_logon_launcher(std::wstring username = L"",
std::wstring password = L"",
std::wstring domain = L"",
DWORD logon_flags = 0u) :
username(std::move(username)),
password(std::move(password)),
domain(std::move(domain)),
logon_flags(logon_flags)
{
}
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 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>
{
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<
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, "with_logon_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 command_line = this->build_command_line(executable, args);
ec = detail::on_setup(*this, executable, command_line, inits...);
if (ec)
{
detail::on_error(*this, executable, command_line, ec, inits...);
return basic_process<Executor>(exec);
}
auto ok = ::CreateProcessWithLogonW(
username.c_str(),
domain.empty() ? nullptr : domain.c_str(),
password.c_str(),
logon_flags,
executable.empty() ? nullptr : executable.c_str(),
command_line.empty() ? nullptr : &command_line.front(),
creation_flags,
environment,
current_directory.empty() ? nullptr : current_directory.c_str(),
&startup_info.StartupInfo,
&process_information);
if (ok == 0)
{
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
detail::on_error(*this, executable, command_line, ec, inits...);
if (process_information.hProcess != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hProcess);
if (process_information.hThread != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hThread);
return basic_process<Executor>(exec);
} else
{
detail::on_success(*this, executable, command_line, inits...);
if (process_information.hThread != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hThread);
return basic_process<Executor>(exec,
this->process_information.dwProcessId,
this->process_information.hProcess);
}
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_WINDOWS_WITH_LOGON_LAUNCHER_HPP

View File

@@ -0,0 +1,135 @@
//
// boost/process/v2/windows/default_launcher.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_WINDOWS_WITH_TOKEN_LAUNCHER_HPP
#define BOOST_PROCESS_V2_WINDOWS_WITH_TOKEN_LAUNCHER_HPP
#include <boost/process/v2/windows/default_launcher.hpp>
BOOST_PROCESS_V2_BEGIN_NAMESPACE
namespace windows
{
/// A windows launcher using CreateProcessWithToken instead of CreateProcess
struct with_token_launcher : default_launcher
{
HANDLE token;
DWORD logon_flags;
with_token_launcher(HANDLE token = INVALID_HANDLE_VALUE,
DWORD logon_flags = 0u) : token(token), logon_flags(logon_flags) {}
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, "with_token_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)
detail::throw_error(ec, "with_token_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 command_line = this->build_command_line(executable, args);
ec = detail::on_setup(*this, executable, command_line, inits...);
if (ec)
{
detail::on_error(*this, executable, command_line, ec, inits...);
return basic_process<Executor>(exec);
}
auto ok = ::CreateProcessWithTokenW(
token,
logon_flags,
executable.empty() ? nullptr : executable.c_str(),
command_line.empty() ? nullptr : &command_line.front(),
creation_flags,
environment,
current_directory.empty() ? nullptr : current_directory.c_str(),
&startup_info.StartupInfo,
&process_information);
if (ok == 0)
{
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
detail::on_error(*this, executable, command_line, ec, inits...);
if (process_information.hProcess != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hProcess);
if (process_information.hThread != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hThread);
return basic_process<Executor>(exec);
} else
{
detail::on_success(*this, executable, command_line, inits...);
if (process_information.hThread != INVALID_HANDLE_VALUE)
::CloseHandle(process_information.hThread);
return basic_process<Executor>(exec,
this->process_information.dwProcessId,
this->process_information.hProcess);
}
}
};
}
BOOST_PROCESS_V2_END_NAMESPACE
#endif // BOOST_PROCESS_V2_WINDOWS_WITH_TOKEN_LAUNCHER_HPP