整理
This commit is contained in:
240
include/boost/process/v2/bind_launcher.hpp
Normal file
240
include/boost/process/v2/bind_launcher.hpp
Normal 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
|
||||
232
include/boost/process/v2/cstring_ref.hpp
Normal file
232
include/boost/process/v2/cstring_ref.hpp
Normal 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
|
||||
67
include/boost/process/v2/default_launcher.hpp
Normal file
67
include/boost/process/v2/default_launcher.hpp
Normal 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
|
||||
182
include/boost/process/v2/detail/config.hpp
Normal file
182
include/boost/process/v2/detail/config.hpp
Normal 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
|
||||
90
include/boost/process/v2/detail/environment_posix.hpp
Normal file
90
include/boost/process/v2/detail/environment_posix.hpp
Normal 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
|
||||
220
include/boost/process/v2/detail/environment_win.hpp
Normal file
220
include/boost/process/v2/detail/environment_win.hpp
Normal 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
|
||||
22
include/boost/process/v2/detail/last_error.hpp
Normal file
22
include/boost/process/v2/detail/last_error.hpp
Normal 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
|
||||
355
include/boost/process/v2/detail/process_handle_fd.hpp
Normal file
355
include/boost/process/v2/detail/process_handle_fd.hpp
Normal 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
|
||||
413
include/boost/process/v2/detail/process_handle_fd_or_signal.hpp
Normal file
413
include/boost/process/v2/detail/process_handle_fd_or_signal.hpp
Normal 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
|
||||
387
include/boost/process/v2/detail/process_handle_signal.hpp
Normal file
387
include/boost/process/v2/detail/process_handle_signal.hpp
Normal 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
|
||||
326
include/boost/process/v2/detail/process_handle_windows.hpp
Normal file
326
include/boost/process/v2/detail/process_handle_windows.hpp
Normal 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
|
||||
33
include/boost/process/v2/detail/throw_error.hpp
Normal file
33
include/boost/process/v2/detail/throw_error.hpp
Normal 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
|
||||
39
include/boost/process/v2/detail/throw_exception.hpp
Normal file
39
include/boost/process/v2/detail/throw_exception.hpp
Normal 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
|
||||
94
include/boost/process/v2/detail/utf8.hpp
Normal file
94
include/boost/process/v2/detail/utf8.hpp
Normal 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
|
||||
1902
include/boost/process/v2/environment.hpp
Normal file
1902
include/boost/process/v2/environment.hpp
Normal file
File diff suppressed because it is too large
Load Diff
44
include/boost/process/v2/error.hpp
Normal file
44
include/boost/process/v2/error.hpp
Normal 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
|
||||
121
include/boost/process/v2/execute.hpp
Normal file
121
include/boost/process/v2/execute.hpp
Normal 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
|
||||
135
include/boost/process/v2/exit_code.hpp
Normal file
135
include/boost/process/v2/exit_code.hpp
Normal 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
|
||||
16
include/boost/process/v2/ext.hpp
Normal file
16
include/boost/process/v2/ext.hpp
Normal 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
|
||||
66
include/boost/process/v2/ext/cmd.hpp
Normal file
66
include/boost/process/v2/ext/cmd.hpp
Normal 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
|
||||
55
include/boost/process/v2/ext/cwd.hpp
Normal file
55
include/boost/process/v2/ext/cwd.hpp
Normal 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
|
||||
119
include/boost/process/v2/ext/detail/proc_info.hpp
Normal file
119
include/boost/process/v2/ext/detail/proc_info.hpp
Normal 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
|
||||
|
||||
148
include/boost/process/v2/ext/env.hpp
Normal file
148
include/boost/process/v2/ext/env.hpp
Normal 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
|
||||
58
include/boost/process/v2/ext/exe.hpp
Normal file
58
include/boost/process/v2/ext/exe.hpp
Normal 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
|
||||
|
||||
66
include/boost/process/v2/pid.hpp
Normal file
66
include/boost/process/v2/pid.hpp
Normal 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
|
||||
|
||||
473
include/boost/process/v2/popen.hpp
Normal file
473
include/boost/process/v2/popen.hpp
Normal 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
|
||||
113
include/boost/process/v2/posix/bind_fd.hpp
Normal file
113
include/boost/process/v2/posix/bind_fd.hpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_BIND_FD_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_BIND_FD_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/default_launcher.hpp>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
/// Utility class to bind a file descriptor to an explicit file descriptor for the child process.
|
||||
struct bind_fd
|
||||
{
|
||||
int target;
|
||||
int fd;
|
||||
bool fd_needs_closing{false};
|
||||
|
||||
~bind_fd()
|
||||
{
|
||||
if (fd_needs_closing)
|
||||
::close(fd);
|
||||
}
|
||||
|
||||
bind_fd() = delete;
|
||||
/// Inherit file descriptor with the same value.
|
||||
/**
|
||||
* This will pass descriptor 42 as 42 to the child process:
|
||||
* @code
|
||||
* process p{"test", {}, posix::bind_fd(42)};
|
||||
* @endcode
|
||||
*/
|
||||
bind_fd(int target) : target(target), fd(target) {}
|
||||
|
||||
/// Inherit an asio io-object as a given file descriptor to the child process.
|
||||
/**
|
||||
* This will pass the tcp::socket, as 42 to the child process:
|
||||
* @code
|
||||
* extern tcp::socket sock;
|
||||
* process p{"test", {}, posix::bind_fd(42, sock)};
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
template<typename Stream>
|
||||
bind_fd(int target, Stream && str, decltype(std::declval<Stream>().native_handle()) = -1)
|
||||
: bind_fd(target, str.native_handle())
|
||||
{}
|
||||
|
||||
/// Inherit a `FILE` as a given file descriptor to the child process.
|
||||
/**
|
||||
* This will pass the given `FILE*`, as 42 to the child process:
|
||||
* @code
|
||||
* process p{"test", {}, posix::bind_fd(42, stderr)};
|
||||
* @endcode
|
||||
*/
|
||||
bind_fd(int target, FILE * f) : bind_fd(target, fileno(f)) {}
|
||||
|
||||
/// Inherit a file descriptor with as a different value.
|
||||
/**
|
||||
* This will pass 24 as 42 to the child process:
|
||||
* @code
|
||||
* process p{"test", {}, posix::bind_fd(42, 24)};
|
||||
* @endcode
|
||||
*/
|
||||
bind_fd(int target, int fd) : target(target), fd(fd) {}
|
||||
|
||||
/// Inherit a null device as a set descriptor.
|
||||
/**
|
||||
* This will pass 24 as 42 to the child process:
|
||||
* @code
|
||||
* process p{"test", {}, posix::bind_fd(42, nullptr)};
|
||||
* @endcode
|
||||
*/
|
||||
bind_fd(int target, std::nullptr_t) : bind_fd(target, filesystem::path("/dev/null")) {}
|
||||
|
||||
/// Inherit a newly opened-file as a set descriptor.
|
||||
/**
|
||||
* This will pass 24 as 42 to the child process:
|
||||
* @code
|
||||
* process p{"test", {}, posix::bind_fd(42, "extra-output.txt")};
|
||||
* @endcode
|
||||
*/
|
||||
bind_fd(int target, const filesystem::path & pth, int flags = O_RDWR | O_CREAT)
|
||||
: target(target), fd(::open(pth.c_str(), flags, 0660)), fd_needs_closing(true)
|
||||
{
|
||||
}
|
||||
|
||||
error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||
{
|
||||
launcher.fd_whitelist.push_back(target);
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Implementation of the initialization function.
|
||||
error_code on_exec_setup(posix::default_launcher & /*launcher*/, const filesystem::path &, const char * const *)
|
||||
{
|
||||
if (::dup2(fd, target) == -1)
|
||||
return error_code(errno, system_category());
|
||||
return error_code ();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_BIND_FD_HPP
|
||||
503
include/boost/process/v2/posix/default_launcher.hpp
Normal file
503
include/boost/process/v2/posix/default_launcher.hpp
Normal file
@@ -0,0 +1,503 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER
|
||||
#define BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
#include <boost/process/v2/posix/detail/close_handles.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/detail/utf8.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/execution/executor.hpp>
|
||||
#include <asio/is_executor.hpp>
|
||||
#include <asio/execution_context.hpp>
|
||||
#include <asio/execution/context.hpp>
|
||||
#include <asio/query.hpp>
|
||||
#else
|
||||
#include <boost/asio/execution/executor.hpp>
|
||||
#include <boost/asio/is_executor.hpp>
|
||||
#include <boost/asio/execution_context.hpp>
|
||||
#include <boost/asio/execution/context.hpp>
|
||||
#include <boost/asio/query.hpp>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <crt_externs.h>
|
||||
# if !defined(environ)
|
||||
# define environ (*_NSGetEnviron())
|
||||
# endif
|
||||
#elif defined(__MACH__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun)
|
||||
extern "C" { extern char **environ; }
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
template<typename Executor>
|
||||
struct basic_process;
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
struct base {};
|
||||
struct derived : base {};
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline error_code invoke_on_setup(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
Init && /*init*/, base && )
|
||||
{
|
||||
return error_code{};
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
Init && init, derived && )
|
||||
-> decltype(init.on_setup(launcher, executable, cmd_line))
|
||||
{
|
||||
return init.on_setup(launcher, executable, cmd_line);
|
||||
}
|
||||
|
||||
template<typename Launcher>
|
||||
inline error_code on_setup(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/))
|
||||
{
|
||||
return error_code{};
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init1, typename ... Inits>
|
||||
inline error_code on_setup(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
Init1 && init1, Inits && ... inits)
|
||||
{
|
||||
auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{});
|
||||
if (ec)
|
||||
return ec;
|
||||
else
|
||||
return on_setup(launcher, executable, cmd_line, inits...);
|
||||
}
|
||||
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline void invoke_on_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
const error_code & /*ec*/, Init && /*init*/, base && )
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
const error_code & ec, Init && init, derived && )
|
||||
-> decltype(init.on_error(launcher, executable, cmd_line, ec))
|
||||
{
|
||||
init.on_error(launcher, executable, cmd_line, ec);
|
||||
}
|
||||
|
||||
template<typename Launcher>
|
||||
inline void on_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
const error_code & /*ec*/)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init1, typename ... Inits>
|
||||
inline void on_error(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
const error_code & ec,
|
||||
Init1 && init1, Inits && ... inits)
|
||||
{
|
||||
invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{});
|
||||
on_error(launcher, executable, cmd_line, ec, inits...);
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline void invoke_on_success(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
Init && /*init*/, base && )
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
Init && init, derived && )
|
||||
-> decltype(init.on_success(launcher, executable, cmd_line))
|
||||
{
|
||||
init.on_success(launcher, executable, cmd_line);
|
||||
}
|
||||
|
||||
template<typename Launcher>
|
||||
inline void on_success(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init1, typename ... Inits>
|
||||
inline void on_success(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
Init1 && init1, Inits && ... inits)
|
||||
{
|
||||
invoke_on_success(launcher, executable, cmd_line, init1, derived{});
|
||||
on_success(launcher, executable, cmd_line, inits...);
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline void invoke_on_fork_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
const error_code & /*ec*/, Init && /*init*/, base && )
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline auto invoke_on_fork_error(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
const error_code & ec, Init && init, derived && )
|
||||
-> decltype(init.on_fork_error(launcher, executable, cmd_line, ec))
|
||||
{
|
||||
init.on_fork_error(launcher, executable, cmd_line, ec);
|
||||
}
|
||||
|
||||
template<typename Launcher>
|
||||
inline void on_fork_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
const error_code & /*ec*/)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init1, typename ... Inits>
|
||||
inline void on_fork_error(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
const error_code & ec,
|
||||
Init1 && init1, Inits && ... inits)
|
||||
{
|
||||
invoke_on_fork_error(launcher, executable, cmd_line, ec, init1, derived{});
|
||||
on_fork_error(launcher, executable, cmd_line, ec, inits...);
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline error_code invoke_on_exec_setup(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
Init && /*init*/, base && )
|
||||
{
|
||||
return error_code{};
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline auto invoke_on_exec_setup(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
Init && init, derived && )
|
||||
-> decltype(init.on_exec_setup(launcher, executable, cmd_line))
|
||||
{
|
||||
return init.on_exec_setup(launcher, executable, cmd_line);
|
||||
}
|
||||
|
||||
template<typename Launcher>
|
||||
inline error_code on_exec_setup(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/))
|
||||
{
|
||||
return error_code{};
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init1, typename ... Inits>
|
||||
inline error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
Init1 && init1, Inits && ... inits)
|
||||
{
|
||||
auto ec = invoke_on_exec_setup(launcher, executable, cmd_line, init1, derived{});
|
||||
if (ec)
|
||||
return ec;
|
||||
else
|
||||
return on_exec_setup(launcher, executable, cmd_line, inits...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline void invoke_on_exec_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
const error_code & /*ec*/, Init && /*init*/, base && )
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init>
|
||||
inline auto invoke_on_exec_error(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
const error_code & ec, Init && init, derived && )
|
||||
-> decltype(init.on_exec_error(launcher, executable, cmd_line, ec))
|
||||
{
|
||||
init.on_exec_error(launcher, executable, cmd_line, ec);
|
||||
}
|
||||
|
||||
template<typename Launcher>
|
||||
inline void on_exec_error(Launcher & /*launcher*/, const filesystem::path &/*executable*/,
|
||||
const char * const * (&/*cmd_line*/),
|
||||
const error_code & /*ec*/)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Launcher, typename Init1, typename ... Inits>
|
||||
inline void on_exec_error(Launcher & launcher, const filesystem::path &executable,
|
||||
const char * const * (&cmd_line),
|
||||
const error_code & ec,
|
||||
Init1 && init1, Inits && ... inits)
|
||||
{
|
||||
invoke_on_exec_error(launcher, executable, cmd_line, ec, init1, derived{});
|
||||
on_exec_error(launcher, executable, cmd_line, ec, inits...);
|
||||
}
|
||||
}
|
||||
|
||||
/// The default launcher for processes on windows.
|
||||
struct default_launcher
|
||||
{
|
||||
/// The pointer to the environment forwarded to the subprocess.
|
||||
const char * const * env = environ;
|
||||
/// The pid of the subprocess - will be assigned after fork.
|
||||
int pid = -1;
|
||||
|
||||
/// The whitelist for file descriptors.
|
||||
std::vector<int> fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
|
||||
|
||||
default_launcher() = default;
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
const typename std::enable_if<std::is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<std::is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
{
|
||||
pipe_guard pg;
|
||||
if (::pipe(pg.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ec = detail::on_setup(*this, executable, argv, inits ...);
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
::close(pg.p[0]);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
#endif
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
{
|
||||
close_all_fds(ec);
|
||||
}
|
||||
if (!ec)
|
||||
::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
|
||||
|
||||
ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
::close(pg.p[1]);
|
||||
pg.p[1] = -1;
|
||||
int child_error{0};
|
||||
int count = -1;
|
||||
while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if ((err != EAGAIN) && (err != EINTR))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category());
|
||||
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
}
|
||||
basic_process<Executor> proc(exec, pid);
|
||||
detail::on_success(*this, executable, argv, ec, inits...);
|
||||
return proc;
|
||||
|
||||
}
|
||||
protected:
|
||||
|
||||
void ignore_unused(std::size_t ) {}
|
||||
void close_all_fds(error_code & ec)
|
||||
{
|
||||
std::sort(fd_whitelist.begin(), fd_whitelist.end());
|
||||
detail::close_all(fd_whitelist, ec);
|
||||
fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
|
||||
}
|
||||
|
||||
struct pipe_guard
|
||||
{
|
||||
int p[2];
|
||||
pipe_guard() : p{-1,-1} {}
|
||||
|
||||
~pipe_guard()
|
||||
{
|
||||
if (p[0] != -1)
|
||||
::close(p[0]);
|
||||
if (p[1] != -1)
|
||||
::close(p[1]);
|
||||
}
|
||||
};
|
||||
|
||||
//if we need to allocate something
|
||||
std::vector<std::string> argv_buffer_;
|
||||
std::vector<const char *> argv_;
|
||||
|
||||
template<typename Args>
|
||||
const char * const * build_argv_(const filesystem::path & pt, const Args & args,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
decltype(*std::begin(std::declval<Args>())),
|
||||
cstring_ref>::value>::type * = nullptr)
|
||||
{
|
||||
const auto arg_cnt = std::distance(std::begin(args), std::end(args));
|
||||
argv_.reserve(arg_cnt + 2);
|
||||
argv_.push_back(pt.native().data());
|
||||
for (auto && arg : args)
|
||||
argv_.push_back(arg.c_str());
|
||||
|
||||
argv_.push_back(nullptr);
|
||||
return argv_.data();
|
||||
}
|
||||
|
||||
const char * const * build_argv_(const filesystem::path &, const char ** argv)
|
||||
{
|
||||
return argv;
|
||||
}
|
||||
|
||||
template<typename Args>
|
||||
const char * const * build_argv_(const filesystem::path & pt, const Args & args,
|
||||
typename std::enable_if<
|
||||
!std::is_convertible<
|
||||
decltype(*std::begin(std::declval<Args>())),
|
||||
cstring_ref>::value>::type * = nullptr)
|
||||
{
|
||||
const auto arg_cnt = std::distance(std::begin(args), std::end(args));
|
||||
argv_.reserve(arg_cnt + 2);
|
||||
argv_buffer_.reserve(arg_cnt);
|
||||
argv_.push_back(pt.native().data());
|
||||
|
||||
using char_type = typename decay<decltype((*std::begin(std::declval<Args>()))[0])>::type;
|
||||
|
||||
for (basic_string_view<char_type> arg : args)
|
||||
argv_buffer_.push_back(v2::detail::conv_string<char>(arg.data(), arg.size()));
|
||||
|
||||
for (auto && arg : argv_buffer_)
|
||||
argv_.push_back(arg.c_str());
|
||||
|
||||
argv_.push_back(nullptr);
|
||||
return argv_.data();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER
|
||||
29
include/boost/process/v2/posix/detail/close_handles.hpp
Normal file
29
include/boost/process/v2/posix/detail/close_handles.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <vector>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// whitelist must be ordered
|
||||
BOOST_PROCESS_V2_DECL void close_all(const std::vector<int> & whitelist,
|
||||
error_code & ec);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_DETAIL_CLOSE_HANDLES_HPP
|
||||
144
include/boost/process/v2/posix/fork_and_forget_launcher.hpp
Normal file
144
include/boost/process/v2/posix/fork_and_forget_launcher.hpp
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_FORK_AND_FORGET_LAUNCHER_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_FORK_AND_FORGET_LAUNCHER_HPP
|
||||
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
/// A posix fork launcher that ignores errors after `fork`.
|
||||
struct fork_and_forget_launcher : default_launcher
|
||||
{
|
||||
fork_and_forget_launcher() = default;
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "fork_and_forget_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "fork_and_forget_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
{
|
||||
ec = detail::on_setup(*this, executable, argv, inits ...);
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
#endif
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
close_all_fds(ec);
|
||||
if (!ec)
|
||||
::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
}
|
||||
basic_process<Executor> proc(exec, pid);
|
||||
detail::on_success(*this, executable, argv, ec, inits...);
|
||||
return proc;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_FORK_AND_FORGET_LAUNCHER_HPP
|
||||
180
include/boost/process/v2/posix/pdfork_launcher.hpp
Normal file
180
include/boost/process/v2/posix/pdfork_launcher.hpp
Normal file
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_PDFORK_LAUNCHER_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_PDFORK_LAUNCHER_HPP
|
||||
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/procdesc.h>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
/// A launcher using `pdfork`. Default on FreeBSD
|
||||
struct pdfork_launcher : default_launcher
|
||||
{
|
||||
/// The file descriptor of the subprocess. Set after fork.
|
||||
int fd;
|
||||
pdfork_launcher() = default;
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pdfork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pdfork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
{
|
||||
pipe_guard pg;
|
||||
if (::pipe(pg.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ec = detail::on_setup(*this, executable, argv, inits ...);
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::pdfork(&fd, PD_DAEMON | PD_CLOEXEC);
|
||||
if (pid == -1)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
#endif
|
||||
::close(pg.p[0]);
|
||||
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
{
|
||||
close_all_fds(ec);
|
||||
}
|
||||
if (!ec)
|
||||
::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
|
||||
|
||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
::close(pg.p[1]);
|
||||
pg.p[1] = -1;
|
||||
int child_error{0};
|
||||
int count = -1;
|
||||
while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if ((err != EAGAIN) && (err != EINTR))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category());
|
||||
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
}
|
||||
basic_process<Executor> proc(exec, pid, fd);
|
||||
detail::on_success(*this, executable, argv, ec, inits...);
|
||||
return proc;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_PDFORK_LAUNCHER_HPP
|
||||
185
include/boost/process/v2/posix/pipe_fork_launcher.hpp
Normal file
185
include/boost/process/v2/posix/pipe_fork_launcher.hpp
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
/// A launcher using `pipe_fork`. Default on FreeBSD
|
||||
struct pipe_fork_launcher : default_launcher
|
||||
{
|
||||
/// The file descriptor of the subprocess. Set after fork.
|
||||
pipe_fork_launcher() = default;
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pipe_fork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "pipe_fork_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
int fd = -1;
|
||||
{
|
||||
pipe_guard pg, pg_wait;
|
||||
if (::pipe(pg.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::pipe(pg_wait.p))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
if (::fcntl(pg_wait.p[1], F_SETFD, FD_CLOEXEC))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ec = detail::on_setup(*this, executable, argv, inits ...);
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
fd_whitelist.push_back(pg_wait.p[1]);
|
||||
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
pid = ::fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
ctx.notify_fork(net::execution_context::fork_child);
|
||||
::close(pg.p[0]);
|
||||
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
{
|
||||
close_all_fds(ec);
|
||||
}
|
||||
if (!ec)
|
||||
::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
|
||||
|
||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
::close(pg.p[1]);
|
||||
pg.p[1] = -1;
|
||||
::close(pg_wait.p[1]);
|
||||
pg_wait.p[1] = -1;
|
||||
int child_error{0};
|
||||
int count = -1;
|
||||
while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if ((err != EAGAIN) && (err != EINTR))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category());
|
||||
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
std::swap(fd, pg_wait.p[0]);
|
||||
}
|
||||
|
||||
basic_process<Executor> proc(exec, pid, fd);
|
||||
detail::on_success(*this, executable, argv, ec, inits...);
|
||||
return proc;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_PIPE_FORK_LAUNCHER_HPP
|
||||
140
include/boost/process/v2/posix/vfork_launcher.hpp
Normal file
140
include/boost/process/v2/posix/vfork_launcher.hpp
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_POSIX_VFORK_LAUNCHER_HPP
|
||||
#define BOOST_PROCESS_V2_POSIX_VFORK_LAUNCHER_HPP
|
||||
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace posix
|
||||
{
|
||||
|
||||
|
||||
/// A launcher using vfork instead of fork.
|
||||
struct vfork_launcher : default_launcher
|
||||
{
|
||||
vfork_launcher() = default;
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
const typename std::enable_if<std::is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
template<typename ExecutionContext, typename Args, typename ... Inits>
|
||||
auto operator()(ExecutionContext & context,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<std::is_convertible<
|
||||
ExecutionContext&, net::execution_context&>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
|
||||
{
|
||||
return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
error_code ec;
|
||||
auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
|
||||
|
||||
if (ec)
|
||||
v2::detail::throw_error(ec, "default_launcher");
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
template<typename Executor, typename Args, typename ... Inits>
|
||||
auto operator()(Executor exec,
|
||||
error_code & ec,
|
||||
const typename std::enable_if<
|
||||
net::execution::is_executor<Executor>::value ||
|
||||
net::is_executor<Executor>::value,
|
||||
filesystem::path >::type & executable,
|
||||
Args && args,
|
||||
Inits && ... inits ) -> basic_process<Executor>
|
||||
{
|
||||
auto argv = this->build_argv_(executable, std::forward<Args>(args));
|
||||
|
||||
ec = detail::on_setup(*this, executable, argv, inits ...);
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
|
||||
auto & ctx = net::query(exec, net::execution::context);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::vfork();
|
||||
if (pid == -1)
|
||||
{
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
detail::on_fork_error(*this, executable, argv, ec, inits...);
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
ec = detail::on_exec_setup(*this, executable, argv, inits...);
|
||||
if (!ec)
|
||||
close_all_fds(ec);
|
||||
if (!ec)
|
||||
::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
#endif
|
||||
if (ec)
|
||||
{
|
||||
detail::on_error(*this, executable, argv, ec, inits...);
|
||||
do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
|
||||
basic_process<Executor> proc(exec, pid);
|
||||
detail::on_success(*this, executable, argv, ec, inits...);
|
||||
return proc;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_POSIX_VFORK_LAUNCHER_HPP
|
||||
403
include/boost/process/v2/process.hpp
Normal file
403
include/boost/process/v2/process.hpp
Normal 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
|
||||
158
include/boost/process/v2/process_handle.hpp
Normal file
158
include/boost/process/v2/process_handle.hpp
Normal 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
|
||||
143
include/boost/process/v2/shell.hpp
Normal file
143
include/boost/process/v2/shell.hpp
Normal 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
|
||||
51
include/boost/process/v2/start_dir.hpp
Normal file
51
include/boost/process/v2/start_dir.hpp
Normal 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
|
||||
367
include/boost/process/v2/stdio.hpp
Normal file
367
include/boost/process/v2/stdio.hpp
Normal 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
|
||||
144
include/boost/process/v2/windows/as_user_launcher.hpp
Normal file
144
include/boost/process/v2/windows/as_user_launcher.hpp
Normal 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
|
||||
50
include/boost/process/v2/windows/creation_flags.hpp
Normal file
50
include/boost/process/v2/windows/creation_flags.hpp
Normal 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
|
||||
451
include/boost/process/v2/windows/default_launcher.hpp
Normal file
451
include/boost/process/v2/windows/default_launcher.hpp
Normal 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
|
||||
53
include/boost/process/v2/windows/show_window.hpp
Normal file
53
include/boost/process/v2/windows/show_window.hpp
Normal 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
|
||||
140
include/boost/process/v2/windows/with_logon_launcher.hpp
Normal file
140
include/boost/process/v2/windows/with_logon_launcher.hpp
Normal 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
|
||||
135
include/boost/process/v2/windows/with_token_launcher.hpp
Normal file
135
include/boost/process/v2/windows/with_token_launcher.hpp
Normal 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
|
||||
Reference in New Issue
Block a user