mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
27 Commits
boost-1.87
...
pty
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
626dd5f1bc | ||
|
|
e4070119ce | ||
|
|
c8499e02c6 | ||
|
|
adf3d62786 | ||
|
|
22c2ad08c9 | ||
|
|
1bbdde8d04 | ||
|
|
e0158d8006 | ||
|
|
07af239503 | ||
|
|
8826a7502d | ||
|
|
e1e594540f | ||
|
|
15d0275b87 | ||
|
|
1e41629759 | ||
|
|
7777db84a8 | ||
|
|
b20e874701 | ||
|
|
606d4d89e4 | ||
|
|
05e8eb3005 | ||
|
|
efd5cc85a5 | ||
|
|
e68491df4d | ||
|
|
02044bcad7 | ||
|
|
41a4a5acc9 | ||
|
|
fae23e60b1 | ||
|
|
34cfc32311 | ||
|
|
4d28e34dfa | ||
|
|
0501b88d7e | ||
|
|
9515f4e1df | ||
|
|
26906fe9f9 | ||
|
|
e8b59f66e6 |
@@ -11,7 +11,7 @@
|
||||
]
|
||||
]
|
||||
|
||||
[note Process v1 will be deprecated in the next release (1.88). Use v2 for new projects.]
|
||||
[note Process v1 will be deprecated in the future. Use v2 for new projects.]
|
||||
|
||||
[include v1.qbk]
|
||||
[include v2.qbk]
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
[include v1/design.qbk]
|
||||
[include v1/extend.qbk]
|
||||
[include v1/faq.qbk]
|
||||
[xinclude reference_v1.xml]
|
||||
[xinclude reference_v2.xml]
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -308,7 +308,7 @@ struct basic_process_handle_win
|
||||
public:
|
||||
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>())
|
||||
auto async_wait(WaitHandler &&handler = default_completion_token_t<executor_type>())
|
||||
-> decltype(net::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{handle_}, handler, handle_))
|
||||
{
|
||||
|
||||
741
include/boost/process/v2/experimental/basic_pty.hpp
Normal file
741
include/boost/process/v2/experimental/basic_pty.hpp
Normal file
@@ -0,0 +1,741 @@
|
||||
//
|
||||
// Copyright (c) 2024 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_VS_EXPERIMENTAL_BASIC_PTY_HPP
|
||||
#define BOOST_PROCESS_VS_EXPERIMENTAL_BASIC_PTY_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/experimental/console_size.hpp>
|
||||
|
||||
#define NTDDI_VERSION 0x0A000006
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS) && !defined(NTDDI_VERSION)
|
||||
#include <sdkddkver.h>
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_WINDOWS) || (NTDDI_VERSION >= 0x0A000006)
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/basic_readable_pipe.hpp>
|
||||
#include <asio/basic_writable_pipe.hpp>
|
||||
#include <asio/connect_pipe.hpp>
|
||||
#else
|
||||
#include <boost/asio/basic_readable_pipe.hpp>
|
||||
#include <boost/asio/basic_writable_pipe.hpp>
|
||||
#include <boost/asio/connect_pipe.hpp>
|
||||
#endif
|
||||
#include <boost/process/v2/windows/default_launcher.hpp>
|
||||
#else
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/posix/basic_stream_descriptor.hpp>
|
||||
#else
|
||||
#include <boost/asio/posix/basic_stream_descriptor.hpp>
|
||||
#endif
|
||||
#include <boost/process/v2/posix/default_launcher.hpp>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
|
||||
template<typename Executor = net::any_io_executor>
|
||||
class basic_pty
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
struct handle_t_
|
||||
{
|
||||
net::basic_readable_pipe<Executor> rm, rs{rm.get_executor()};
|
||||
net::basic_writable_pipe<Executor> wm{rm.get_executor()}, ws{rm.get_executor()};
|
||||
template<typename Arg>
|
||||
handle_t_(Arg && arg) : rm{arg} {}
|
||||
|
||||
bool is_open() const {return rm.is_open();}
|
||||
};
|
||||
|
||||
handle_t_ handle_;
|
||||
net::basic_readable_pipe<Executor> & read_handle_() {return handle_.rm;}
|
||||
net::basic_writable_pipe<Executor> & write_handle_() {return handle_.wm;}
|
||||
struct hcon_deleter_
|
||||
{
|
||||
void operator()(HPCON con)
|
||||
{
|
||||
ClosePseudoConsole(con);
|
||||
}
|
||||
};
|
||||
std::unique_ptr<void, hcon_deleter_> con_;
|
||||
|
||||
#else
|
||||
net::posix::basic_stream_descriptor<Executor> handle_;
|
||||
|
||||
net::posix::basic_stream_descriptor<Executor> & read_handle_() { return handle_; };
|
||||
net::posix::basic_stream_descriptor<Executor> & write_handle_() { return handle_; };
|
||||
#endif
|
||||
public:
|
||||
/// The type of the executor associated with the object.
|
||||
typedef Executor executor_type;
|
||||
|
||||
/// Rebinds the descriptor type to another executor.
|
||||
template <typename Executor1>
|
||||
struct rebind_executor
|
||||
{
|
||||
/// The descriptor type when rebound to the specified executor.
|
||||
typedef basic_pty<Executor1> other;
|
||||
};
|
||||
|
||||
/// A descriptor is always the lowest layer.
|
||||
typedef basic_pty lowest_layer_type ;
|
||||
|
||||
|
||||
/// Construct a stream descriptor without opening it.
|
||||
/**
|
||||
* This constructor creates a stream descriptor without opening it. The
|
||||
* descriptor needs to be opened and then connected or accepted before data
|
||||
* can be sent or received on it.
|
||||
*
|
||||
* @param ex The I/O executor that the descriptor will use, by default, to
|
||||
* dispatch handlers for any asynchronous operations performed on the
|
||||
* descriptor.
|
||||
*/
|
||||
explicit basic_pty(const executor_type& ex)
|
||||
: handle_(ex)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a stream descriptor without opening it.
|
||||
/**
|
||||
* This constructor creates a stream descriptor without opening it. The
|
||||
* descriptor needs to be opened and then connected or accepted before data
|
||||
* can be sent or received on it.
|
||||
*
|
||||
* @param context An execution context which provides the I/O executor that
|
||||
* the descriptor will use, by default, to dispatch handlers for any
|
||||
* asynchronous operations performed on the descriptor.
|
||||
*/
|
||||
template <typename ExecutionContext>
|
||||
explicit basic_pty(ExecutionContext& context,
|
||||
net::constraint_t<
|
||||
std::is_convertible<ExecutionContext&, net::execution_context&>::value,
|
||||
net::defaulted_constraint
|
||||
> = net::defaulted_constraint())
|
||||
: handle_(context)
|
||||
{
|
||||
}
|
||||
|
||||
/// Move-construct a stream descriptor from another.
|
||||
/**
|
||||
* This constructor moves a stream descriptor from one object to another.
|
||||
*
|
||||
* @param other The other stream descriptor object from which the move
|
||||
* will occur.
|
||||
*
|
||||
* @note Following the move, the moved-from object is in the same state as if
|
||||
* constructed using the @c basic_pty(const executor_type&)
|
||||
* constructor.
|
||||
*/
|
||||
basic_pty(basic_pty&& other) noexcept
|
||||
: handle_(std::move(other))
|
||||
{
|
||||
}
|
||||
|
||||
/// Move-assign a stream descriptor from another.
|
||||
/**
|
||||
* This assignment operator moves a stream descriptor from one object to
|
||||
* another.
|
||||
*
|
||||
* @param other The other stream descriptor object from which the move
|
||||
* will occur.
|
||||
*
|
||||
* @note Following the move, the moved-from object is in the same state as if
|
||||
* constructed using the @c basic_pty(const executor_type&)
|
||||
* constructor.
|
||||
*/
|
||||
basic_pty& operator=(basic_pty&& other)
|
||||
{
|
||||
handle_= std::move(other.handle_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Move-construct a basic_pty from a descriptor of another
|
||||
/// executor type.
|
||||
/**
|
||||
* This constructor moves a descriptor from one object to another.
|
||||
*
|
||||
* @param other The other basic_pty object from which the move
|
||||
* will occur.
|
||||
*
|
||||
* @note Following the move, the moved-from object is in the same state as if
|
||||
* constructed using the @c basic_pty(const executor_type&)
|
||||
* constructor.
|
||||
*/
|
||||
template <typename Executor1>
|
||||
basic_pty(basic_pty<Executor1>&& other,
|
||||
net::constraint_t<
|
||||
net::is_convertible<Executor1, Executor>::value,
|
||||
net::defaulted_constraint
|
||||
> = net::defaulted_constraint())
|
||||
: handle_(std::move(other))
|
||||
{
|
||||
}
|
||||
|
||||
/// Move-assign a basic_pty from a descriptor of another
|
||||
/// executor type.
|
||||
/**
|
||||
* This assignment operator moves a descriptor from one object to another.
|
||||
*
|
||||
* @param other The other basic_pty object from which the move
|
||||
* will occur.
|
||||
*
|
||||
* @note Following the move, the moved-from object is in the same state as if
|
||||
* constructed using the @c basic_pty(const executor_type&)
|
||||
* constructor.
|
||||
*/
|
||||
template <typename Executor1>
|
||||
net::constraint_t<
|
||||
net::is_convertible<Executor1, Executor>::value,
|
||||
basic_pty&
|
||||
> operator=(basic_pty<Executor1> && other)
|
||||
{
|
||||
handle_ = std::move(other.handle_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// Get the executor associated with the object.
|
||||
const executor_type& get_executor() noexcept
|
||||
{
|
||||
return handle_.get_executor();
|
||||
}
|
||||
|
||||
/// Get a reference to the lowest layer.
|
||||
/**
|
||||
* This function returns a reference to the lowest layer in a stack of
|
||||
* layers. Since a descriptor cannot contain any further layers, it
|
||||
* simply returns a reference to itself.
|
||||
*
|
||||
* @return A reference to the lowest layer in the stack of layers. Ownership
|
||||
* is not transferred to the caller.
|
||||
*/
|
||||
lowest_layer_type& lowest_layer()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get a const reference to the lowest layer.
|
||||
/**
|
||||
* This function returns a const reference to the lowest layer in a stack of
|
||||
* layers. Since a descriptor cannot contain any further layers, it
|
||||
* simply returns a reference to itself.
|
||||
*
|
||||
* @return A const reference to the lowest layer in the stack of layers.
|
||||
* Ownership is not transferred to the caller.
|
||||
*/
|
||||
const lowest_layer_type& lowest_layer() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Determine whether the descriptor is open.
|
||||
bool is_open() const
|
||||
{
|
||||
return handle_.is_open();
|
||||
}
|
||||
|
||||
/// Close the descriptor.
|
||||
/**
|
||||
* This function is used to close the descriptor. Any asynchronous read or
|
||||
* write operations will be cancelled immediately, and will complete with the
|
||||
* boost::asio::error::operation_aborted error.
|
||||
*
|
||||
* @throws boost::system::system_error Thrown on failure. Note that, even if
|
||||
* the function indicates an error, the underlying descriptor is closed.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
handle_.close();
|
||||
#else
|
||||
handle_.rm.close();
|
||||
handle_.wm.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Close the descriptor.
|
||||
/**
|
||||
* This function is used to close the descriptor. Any asynchronous read or
|
||||
* write operations will be cancelled immediately, and will complete with the
|
||||
* boost::asio::error::operation_aborted error.
|
||||
*
|
||||
* @param ec Set to indicate what error occurred, if any. Note that, even if
|
||||
* the function indicates an error, the underlying descriptor is closed.
|
||||
*/
|
||||
void close(boost::system::error_code& ec)
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
handle_.close(ec);
|
||||
#else
|
||||
handle_.wm.close(ec);
|
||||
handle_.rm.close(ec);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Cancel all asynchronous operations associated with the descriptor.
|
||||
/**
|
||||
* This function causes all outstanding asynchronous read or write operations
|
||||
* to finish immediately, and the handlers for cancelled operations will be
|
||||
* passed the boost::asio::error::operation_aborted error.
|
||||
*
|
||||
* @throws boost::system::system_error Thrown on failure.
|
||||
*/
|
||||
void cancel()
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
handle_.cancel();
|
||||
#else
|
||||
handle_.rm.cancel();
|
||||
handle_.wm.cancel();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Cancel all asynchronous operations associated with the descriptor.
|
||||
/**
|
||||
* This function causes all outstanding asynchronous read or write operations
|
||||
* to finish immediately, and the handlers for cancelled operations will be
|
||||
* passed the boost::asio::error::operation_aborted error.
|
||||
*
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
void cancel(boost::system::error_code& ec)
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
handle_.cancel(ec);
|
||||
#else
|
||||
handle_.rm.cancel(ec);
|
||||
handle_.wm.cancel(ec);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Write some data to the descriptor.
|
||||
/**
|
||||
* This function is used to write data to the stream descriptor. 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 descriptor.
|
||||
*
|
||||
* @returns The number of bytes written.
|
||||
*
|
||||
* @throws boost::system::system_error Thrown on failure. An error code of
|
||||
* boost::asio::error::eof indicates that the connection was closed by the
|
||||
* peer.
|
||||
*
|
||||
* @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
|
||||
* descriptor.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 write_handle_().write_some(buffers);
|
||||
}
|
||||
|
||||
/// Write some data to the descriptor.
|
||||
/**
|
||||
* This function is used to write data to the stream descriptor. 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 descriptor.
|
||||
*
|
||||
* @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
|
||||
* peer. 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,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
return write_handle_().write_some(buffers, ec);
|
||||
}
|
||||
|
||||
/// Start an asynchronous write.
|
||||
/**
|
||||
* This function is used to asynchronously write data to the stream
|
||||
* descriptor. 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 descriptor.
|
||||
* 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 boost::system::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::async_immediate().
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void(boost::system::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
|
||||
* descriptor.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.
|
||||
*
|
||||
* @par Per-Operation Cancellation
|
||||
* This asynchronous operation supports cancellation for the following
|
||||
* boost::asio::cancellation_type values:
|
||||
*
|
||||
* @li @c cancellation_type::terminal
|
||||
*
|
||||
* @li @c cancellation_type::partial
|
||||
*
|
||||
* @li @c cancellation_type::total
|
||||
*/
|
||||
template <typename ConstBufferSequence,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::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(write_handle_().async_write_some(buffers, std::forward<WriteToken>(token)))
|
||||
{
|
||||
return write_handle_().async_write_some(buffers, std::forward<WriteToken>(token));
|
||||
}
|
||||
|
||||
/// Read some data from the descriptor.
|
||||
/**
|
||||
* This function is used to read data from the stream descriptor. 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 boost::system::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
|
||||
* descriptor.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 read_handle_().read_some(buffers);
|
||||
}
|
||||
|
||||
/// Read some data from the descriptor.
|
||||
/**
|
||||
* This function is used to read data from the stream descriptor. 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,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
return read_handle_().read_some(buffers, ec);
|
||||
}
|
||||
|
||||
/// Start an asynchronous read.
|
||||
/**
|
||||
* This function is used to asynchronously read data from the stream
|
||||
* descriptor. 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 boost::system::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::async_immediate().
|
||||
*
|
||||
* @par Completion Signature
|
||||
* @code void(boost::system::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
|
||||
* descriptor.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.
|
||||
*
|
||||
* @par Per-Operation Cancellation
|
||||
* This asynchronous operation supports cancellation for the following
|
||||
* boost::asio::cancellation_type values:
|
||||
*
|
||||
* @li @c cancellation_type::terminal
|
||||
*
|
||||
* @li @c cancellation_type::partial
|
||||
*
|
||||
* @li @c cancellation_type::total
|
||||
*/
|
||||
template <typename MutableBufferSequence,
|
||||
BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
|
||||
std::size_t)) ReadToken = net::default_completion_token_t<executor_type>>
|
||||
auto async_read_some(const MutableBufferSequence& buffers,
|
||||
ReadToken&& token = net::default_completion_token_t<executor_type>())
|
||||
-> decltype(read_handle_().async_read_some(buffers, std::forward<ReadToken>(token)))
|
||||
{
|
||||
return read_handle_().async_read_some(buffers, std::forward<ReadToken>(token));
|
||||
}
|
||||
|
||||
void open(console_size_t size, error_code & ec)
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
auto res = posix_openpt(O_RDWR | O_NOCTTY);
|
||||
if (res < 0)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return ;
|
||||
}
|
||||
else
|
||||
handle_.assign(res, ec);
|
||||
|
||||
|
||||
winsize ws{size.rows, size.columns};
|
||||
if (ioctl(res, TIOCSWINSZ, &ws) != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
/* struct termios tios;
|
||||
if (!ec && ioctl(res, TCGETS, &tios))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
#if defined (IUTF8)
|
||||
tios.c_iflag |= IUTF8;
|
||||
#endif
|
||||
tios.c_lflag &= ~(ECHOE | ECHOK | ECHONL | ICANON);
|
||||
|
||||
|
||||
if (!ec && ioctl(res, TCSETSW, &tios))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
*/
|
||||
|
||||
if (ec)
|
||||
return ;
|
||||
|
||||
if (grantpt(res) || unlockpt(res))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
this->close(ec);
|
||||
}
|
||||
#else
|
||||
net::connect_pipe(handle_.rm, handle_.ws, ec); // output
|
||||
if (!ec)
|
||||
{
|
||||
net::connect_pipe(handle_.rs, handle_.wm, ec); // input
|
||||
if (ec)
|
||||
handle_.rm.close(ec);
|
||||
}
|
||||
if (ec)
|
||||
return ;
|
||||
|
||||
HPCON out;
|
||||
auto res = CreatePseudoConsole(
|
||||
COORD{static_cast<short>(size.columns), static_cast<short>(size.rows)},
|
||||
handle_.rs.native_handle(),
|
||||
handle_.ws.native_handle(), 0, &out);
|
||||
if (res != S_OK)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, HRESULT_CODE(res), system_category());
|
||||
else
|
||||
con_.reset(out);
|
||||
#endif
|
||||
}
|
||||
|
||||
void open(console_size_t size)
|
||||
{
|
||||
error_code ec;
|
||||
open(size, ec);
|
||||
if (ec)
|
||||
boost::process::v2::detail::throw_error(ec, "open");
|
||||
}
|
||||
|
||||
void resize(console_size_t size, error_code & ec)
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
winsize ws{size.rows, size.columns, 0, 0};
|
||||
if (ioctl(handle_.native_handle(), TIOCSWINSZ, &ws) != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
#else
|
||||
auto res = ::ResizePseudoConsole(con_.get(),
|
||||
COORD{static_cast<short>(size.columns), static_cast<short>(size.rows)});
|
||||
if (res != S_OK)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, HRESULT_CODE(res), system_category());
|
||||
#endif
|
||||
}
|
||||
|
||||
void resize(console_size_t size)
|
||||
{
|
||||
error_code ec;
|
||||
resize(size, ec);
|
||||
if (ec)
|
||||
boost::process::v2::detail::throw_error(ec, "resize");
|
||||
}
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
error_code on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||
{
|
||||
error_code ec;
|
||||
if (!is_open())
|
||||
this->open(console_size_t{80, 24}, ec);
|
||||
return ec;
|
||||
}
|
||||
|
||||
error_code on_exec_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||
{
|
||||
error_code ec;
|
||||
auto nm = ptsname(handle_.native_handle());
|
||||
if (nm == nullptr)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ENODEV, system_category());
|
||||
else
|
||||
{
|
||||
auto s = ::open(nm, O_WRONLY);
|
||||
if (s < 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
auto t = ::open(nm, O_RDONLY);
|
||||
if (t < 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
if ((::dup2(t, STDIN_FILENO) == -1)
|
||||
|| (::dup2(s, STDOUT_FILENO) == -1)
|
||||
|| (::dup2(s, STDERR_FILENO) == -1)
|
||||
|| (::setsid() == -1)
|
||||
#ifdef TIOCSCTTY
|
||||
|| ::ioctl(t, TIOCSCTTY, 0)
|
||||
#endif
|
||||
|| ::close(handle_.native_handle())
|
||||
|| ::close(s)
|
||||
|| ::close(t))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
}
|
||||
return error_code ();
|
||||
}
|
||||
#else
|
||||
|
||||
error_code on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &)
|
||||
{
|
||||
error_code ec;
|
||||
if (!is_open())
|
||||
open(console_size_t{80, 24}, ec);
|
||||
|
||||
auto &si = launcher.startup_info;
|
||||
launcher.startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||
// https://github.com/microsoft/terminal/issues/11276
|
||||
launcher.startup_info.StartupInfo.hStdOutput = NULL;
|
||||
launcher.startup_info.StartupInfo.hStdError = NULL;
|
||||
launcher.startup_info.StartupInfo.hStdInput = NULL;
|
||||
size_t bytes_required;
|
||||
InitializeProcThreadAttributeList(NULL, 1, 0, &bytes_required);
|
||||
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, bytes_required);
|
||||
if (!si.lpAttributeList)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ENOMEM, system_category());
|
||||
return ec;
|
||||
}
|
||||
|
||||
// Initialize the list memory location
|
||||
if (!InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &bytes_required)
|
||||
|| !UpdateProcThreadAttribute(si.lpAttributeList,
|
||||
0,
|
||||
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
|
||||
con_.get(),
|
||||
sizeof(con_.get()),
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, si.lpAttributeList);
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return ec;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif // windows version
|
||||
#endif //BOOST_PROCESS_VS_EXPERIMENTAL_BASIC_PTY_HPP
|
||||
1085
include/boost/process/v2/experimental/basic_stream.hpp
Normal file
1085
include/boost/process/v2/experimental/basic_stream.hpp
Normal file
File diff suppressed because it is too large
Load Diff
25
include/boost/process/v2/experimental/console_size.hpp
Normal file
25
include/boost/process/v2/experimental/console_size.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright (c) 2024 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_CONSOLE_SIZE_CONSOLE_SIZE_HPP
|
||||
#define BOOST_PROCESS_V2_CONSOLE_SIZE_CONSOLE_SIZE_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
struct console_size_t
|
||||
{
|
||||
unsigned short columns, rows;
|
||||
};
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_CONSOLE_SIZE_CONSOLE_SIZE_HPP
|
||||
@@ -0,0 +1,398 @@
|
||||
//
|
||||
// Copyright (c) 2024 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_EXPERIMENTAL_DETAIL_BASIC_STREAM_HANDLE_HPP
|
||||
#define BOOST_PROCESS_V2_EXPERIMENTAL_DETAIL_BASIC_STREAM_HANDLE_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/experimental/console_size.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/append.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <asio/windows/basic_object_handle.hpp>
|
||||
#include <asio/windows/basic_stream_handle.hpp>
|
||||
#else
|
||||
#include <boost/asio/append.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/asio/windows/basic_object_handle.hpp>
|
||||
#include <boost/asio/windows/basic_stream_handle.hpp>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
namespace detail
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
template<typename Executor>
|
||||
struct basic_stream_handle
|
||||
{
|
||||
net::windows::basic_object_handle<Executor> object;
|
||||
net::windows::basic_stream_handle<Executor> stream;
|
||||
v2::experimental::console_size_t cs_buf_{0u, 0u};
|
||||
|
||||
template<typename Arg>
|
||||
basic_stream_handle(Arg &&arg) : object{arg}, stream{arg} {}
|
||||
|
||||
template<typename Executor1>
|
||||
basic_stream_handle(basic_stream_handle<Executor1> &&lhs)
|
||||
: object(std::move(lhs.object)), stream(std::move(lhs.stream)), cs_buf_(lhs.cs_buf_) {}
|
||||
|
||||
using executor_type = Executor;
|
||||
executor_type get_executor() noexcept { return object.get_executor(); }
|
||||
|
||||
|
||||
void assign(HANDLE h, error_code &ec)
|
||||
{
|
||||
assert(GetFileType(h) == FILE_TYPE_CHAR);
|
||||
if (GetFileType(h) == FILE_TYPE_CHAR)
|
||||
{
|
||||
stream.close(ec);
|
||||
object.assign(h, ec);
|
||||
DWORD flags;
|
||||
if (!::GetConsoleMode(h, &flags)) {
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!::SetConsoleMode(h, flags | ENABLE_VIRTUAL_TERMINAL_INPUT)) // probably output!
|
||||
if (!::SetConsoleMode(h, flags | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING ))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
if (!::SetConsoleCP(CP_UTF8))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
if (!::SetConsoleOutputCP(CP_UTF8))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO bi;
|
||||
if (::GetConsoleScreenBufferInfo(h, &bi))
|
||||
{
|
||||
cs_buf_.columns = bi.dwSize.X;
|
||||
cs_buf_.rows = bi.dwSize.Y;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
object.close(ec);
|
||||
stream.assign(h, ec);
|
||||
}
|
||||
}
|
||||
|
||||
void assign(HANDLE h)
|
||||
{
|
||||
error_code ec;
|
||||
assign(h, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "assign");
|
||||
}
|
||||
|
||||
void close(error_code &ec)
|
||||
{
|
||||
object.close(ec);
|
||||
stream.close(ec);
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
object.close();
|
||||
stream.close();
|
||||
}
|
||||
|
||||
HANDLE native_handle()
|
||||
{
|
||||
return object.is_open() ? object.native_handle() : stream.native_handle();
|
||||
}
|
||||
|
||||
HANDLE release()
|
||||
{
|
||||
return object.is_open() ? object.release() : stream.release();
|
||||
}
|
||||
|
||||
void cancel(error_code &ec)
|
||||
{
|
||||
if (object.is_open())
|
||||
object.cancel(ec);
|
||||
else if (stream.is_open())
|
||||
stream.cancel(ec);
|
||||
|
||||
trigger_size_.cancel(ec);
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
if (object.is_open())
|
||||
object.cancel();
|
||||
else if (stream.is_open())
|
||||
stream.cancel();
|
||||
|
||||
trigger_size_.cancel();
|
||||
}
|
||||
|
||||
bool is_open() const { return object.is_open() || stream.is_open(); }
|
||||
|
||||
void wait(error_code &ec)
|
||||
{
|
||||
if (object.is_open())
|
||||
object.wait(ec);
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::operation_not_supported);
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
error_code ec;
|
||||
wait(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec);
|
||||
}
|
||||
|
||||
struct async_wait_op
|
||||
{
|
||||
template<typename Handler>
|
||||
void operator()(Handler &&handler, basic_stream_handle *this_)
|
||||
{
|
||||
if (this_->object.is_open())
|
||||
this_->object.async_wait(std::move(handler));
|
||||
else {
|
||||
auto e = net::get_associated_immediate_executor(handler, this_->stream.get_executor());
|
||||
net::dispatch(e, net::append(std::move(handler), net::error::operation_not_supported));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Token>
|
||||
auto async_wait(Token &&token)
|
||||
{
|
||||
return net::async_initiate<Token, void(error_code)>(async_wait_op{}, token, this);
|
||||
}
|
||||
|
||||
template<typename MutableBufferSequence>
|
||||
std::size_t read_some(const MutableBufferSequence &mbs, error_code &ec)
|
||||
{
|
||||
if (stream.is_open())
|
||||
return stream.read_some(mbs, ec);
|
||||
|
||||
|
||||
asio::mutable_buffer buf;
|
||||
|
||||
for (auto itr = net::buffer_sequence_begin(mbs);
|
||||
itr != net::buffer_sequence_end(mbs); itr++)
|
||||
if (itr->size() > 0)
|
||||
{
|
||||
buf = *itr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf.size() == 0u)
|
||||
return 0u;
|
||||
|
||||
DWORD read_size = 0u;
|
||||
if (!::ReadFile(object.native_handle(), buf.data(), buf.size(), &read_size, NULL))
|
||||
{
|
||||
const DWORD last_error = ::GetLastError();
|
||||
if (ERROR_END_OF_MEDIA == last_error)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::eof);
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, last_error, system_category());
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>(read_size);
|
||||
}
|
||||
|
||||
template<typename MutableBuffer>
|
||||
struct async_read_some_op
|
||||
{
|
||||
basic_stream_handle *this_;
|
||||
MutableBuffer buffer;
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
if (this_->stream.is_open())
|
||||
return this_->stream.async_read_some(buffer, std::move(self));
|
||||
|
||||
this_->async_wait(std::move(self));
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec)
|
||||
{
|
||||
asio::mutable_buffer buf;
|
||||
|
||||
for (auto itr = net::buffer_sequence_begin(buffer);
|
||||
itr != net::buffer_sequence_end(buffer); itr++)
|
||||
if (itr->size() > 0)
|
||||
{
|
||||
buf = *itr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf.size() == 0u)
|
||||
return self.complete(error_code{}, 0u);
|
||||
|
||||
INPUT_RECORD in_buffer[8092];
|
||||
DWORD read_size = 0u;
|
||||
if (!::PeekConsoleInputW(this_->object.native_handle(), in_buffer, 8092, &read_size))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return self.complete(ec, 0u);
|
||||
}
|
||||
auto begin = in_buffer,
|
||||
end = in_buffer + read_size;
|
||||
|
||||
std::size_t keys = 0u;
|
||||
bool has_cr = false;
|
||||
for (auto itr = begin; itr != end; itr++)
|
||||
{
|
||||
if (itr->EventType == KEY_EVENT)
|
||||
{
|
||||
keys += itr->Event.KeyEvent.wRepeatCount;
|
||||
has_cr |= itr->Event.KeyEvent.uChar.AsciiChar == '\r';
|
||||
}
|
||||
}
|
||||
|
||||
if (keys == 0u)
|
||||
{
|
||||
if (!::FlushConsoleInputBuffer(this_->object.native_handle()))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return self.complete(ec, 0u);
|
||||
}
|
||||
else
|
||||
return this_->async_wait(std::move(self));
|
||||
}
|
||||
|
||||
if (!has_cr)
|
||||
{
|
||||
DWORD mode = 0;
|
||||
if (!::GetConsoleMode(this_->object.native_handle(), &mode))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
else if ((mode & ENABLE_LINE_INPUT) != 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::would_block); // line mode would block on windows
|
||||
}
|
||||
if (ec)
|
||||
return self.complete(ec, 0u);
|
||||
|
||||
DWORD to_read = (std::min)(keys, buf.size());
|
||||
|
||||
read_size = 0u;
|
||||
if (!ReadFile(this_->object.native_handle(), buf.data(), to_read, &read_size, NULL))
|
||||
{
|
||||
const DWORD last_error = ::GetLastError();
|
||||
if (ERROR_END_OF_MEDIA == last_error)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::eof);
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, last_error, system_category());
|
||||
}
|
||||
|
||||
self.complete(ec, static_cast<std::size_t>(read_size));
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec, std::size_t n_)
|
||||
{
|
||||
self.complete(ec, n_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename MutableBuffer, typename CompletionToken>
|
||||
auto async_read_some(const MutableBuffer &buffer, CompletionToken &&token)
|
||||
-> decltype(net::async_initiate<CompletionToken,
|
||||
void(boost::system::error_code, std::size_t)>(
|
||||
net::composed(async_read_some_op<MutableBuffer>{this, buffer}, stream), token))
|
||||
{
|
||||
return net::async_initiate<CompletionToken,
|
||||
void(boost::system::error_code, std::size_t)>(
|
||||
net::composed(async_read_some_op<MutableBuffer>{this, buffer}, stream), token);
|
||||
}
|
||||
|
||||
template<typename ConstBufferSequence>
|
||||
std::size_t write_some(const ConstBufferSequence &cbs, error_code &ec)
|
||||
{
|
||||
if (stream.is_open())
|
||||
return stream.write_some(cbs, ec);
|
||||
|
||||
net::const_buffer buf;
|
||||
for (auto itr = net::buffer_sequence_begin(cbs);
|
||||
itr != net::buffer_sequence_end(cbs); itr++)
|
||||
if (itr->size() > 0)
|
||||
{
|
||||
buf = *itr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf.size() == 0u)
|
||||
return 0u;
|
||||
|
||||
// get one screen size, that's how much we'll write
|
||||
CONSOLE_SCREEN_BUFFER_INFO bi;
|
||||
if (!GetConsoleScreenBufferInfo(object.native_handle(), &bi)) {
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return 0u;
|
||||
}
|
||||
|
||||
const auto sz = (std::min)(static_cast<std::size_t>(bi.dwSize.X * bi.dwSize.Y), buf.size());
|
||||
|
||||
DWORD wr = 0u;
|
||||
if (!WriteConsoleA(object.native_handle(), buf.data(), sz, &wr, NULL))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
return static_cast<std::size_t>(wr);
|
||||
}
|
||||
|
||||
template<typename ConstBufferSequence>
|
||||
struct async_write_op
|
||||
{
|
||||
basic_stream_handle *this_;
|
||||
ConstBufferSequence buffer;
|
||||
|
||||
template<typename Handler>
|
||||
void operator()(Handler &&handler)
|
||||
{
|
||||
if (this_->stream.is_open())
|
||||
return this_->stream.async_write_some(buffer, std::move(handler));
|
||||
|
||||
error_code ec;
|
||||
auto n = this_->write_some(buffer, ec);
|
||||
auto exec = net::get_associated_immediate_executor(
|
||||
handler, this_->object.get_executor());
|
||||
net::dispatch(exec, net::append(std::move(handler), ec, n));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ConstBufferSequence, typename CompletionToken>
|
||||
auto async_write_some(const ConstBufferSequence & buffer, CompletionToken && token)
|
||||
-> decltype(net::async_initiate<CompletionToken,
|
||||
void(boost::system::error_code, std::size_t)>(
|
||||
async_write_op<ConstBufferSequence>{this, buffer}, token))
|
||||
{
|
||||
return net::async_initiate<CompletionToken,
|
||||
void(boost::system::error_code, std::size_t)>(
|
||||
async_write_op<ConstBufferSequence>{this, buffer}, token);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
#endif //BOOST_PROCESS_V2_EXPERIMENTAL_DETAIL_BASIC_STREAM_HANDLE_HPP
|
||||
25
include/boost/process/v2/experimental/pty.hpp
Normal file
25
include/boost/process/v2/experimental/pty.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright (c) 2024 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_VS_EXPERIMENTAL_PTY_HPP
|
||||
#define BOOST_PROCESS_VS_EXPERIMENTAL_PTY_HPP
|
||||
|
||||
#include <boost/process/v2/experimental/basic_pty.hpp>
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_WINDOWS) || (NTDDI_VERSION >= 0x0A000006)
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
typedef basic_pty<> pty;
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif // windows version
|
||||
#endif //BOOST_PROCESS_VS_EXPERIMENTAL_PTY_HPP
|
||||
24
include/boost/process/v2/experimental/stream.hpp
Normal file
24
include/boost/process/v2/experimental/stream.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright (c) 2024 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_EXPERIMENTAL_PROCESS_STREAM_HPP
|
||||
#define BOOST_PROCESS_V2_EXPERIMENTAL_PROCESS_STREAM_HPP
|
||||
|
||||
#include <boost/process/v2/experimental/basic_stream.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
typedef basic_stream<> stream;
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_EXPERIMENTAL_PROCESS_STREAM_HPP
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#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>
|
||||
@@ -27,6 +26,9 @@ namespace ext
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
using native_env_handle_type = wchar_t *;
|
||||
using native_env_iterator = wchar_t *;
|
||||
#elif defined(__FreeBSD__)
|
||||
using native_env_handle_type = char **;
|
||||
using native_env_iterator = char **;
|
||||
#else
|
||||
using native_env_handle_type = char *;
|
||||
using native_env_iterator = char *;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
#endif
|
||||
|
||||
#if defined(__sun)
|
||||
#include <cstdlib>
|
||||
#include <sys/types.h>
|
||||
#include <kvm.h>
|
||||
#include <sys/param.h>
|
||||
@@ -275,7 +274,6 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
const char *nlistf, *memf;
|
||||
nlistf = memf = "/dev/null";
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -285,7 +283,7 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)};
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); return {};}
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return {};}
|
||||
if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, &cntp)))
|
||||
{
|
||||
char **cmd = kvm_getargv(kd.get(), proc_info, 0);
|
||||
@@ -303,9 +301,10 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
|
||||
shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
|
||||
std::vector<std::string> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc2 *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -316,7 +315,7 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)};
|
||||
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); return {};}
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return vec;}
|
||||
if ((proc_info = kvm_getproc2(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc2), &cntp)))
|
||||
{
|
||||
char **cmd = kvm_getargv2(kd.get(), proc_info, 0);
|
||||
@@ -327,13 +326,14 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return {};
|
||||
return vec;
|
||||
}
|
||||
|
||||
#elif defined(__OpenBSD__)
|
||||
|
||||
shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
std::vector<std::string> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
|
||||
@@ -346,7 +346,7 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)};
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); return {};}
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return vec;}
|
||||
if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &cntp)))
|
||||
{
|
||||
char **cmd = kvm_getargv(kd.get(), proc_info, 0);
|
||||
@@ -367,29 +367,22 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
char **cmd = nullptr;
|
||||
proc *proc_info = nullptr;
|
||||
user *proc_user = nullptr;
|
||||
|
||||
struct closer
|
||||
kvm_t *kd = kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr);
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return {};}
|
||||
if ((proc_info = kvm_getproc(kd, pid)))
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
if ((proc_user = kvm_getu(kd, proc_info)))
|
||||
{
|
||||
kvm_close(kd);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr)};
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); return {};}
|
||||
if ((proc_info = kvm_getproc(kd.get(), pid)))
|
||||
{
|
||||
if ((proc_user = kvm_getu(kd.get(), proc_info)))
|
||||
{
|
||||
if (!kvm_getcmd(kd.get(), proc_info, proc_user, &cmd, nullptr))
|
||||
if (!kvm_getcmd(kd, proc_info, proc_user, &cmd, nullptr))
|
||||
{
|
||||
int argc = 0;
|
||||
for (int i = 0; cmd[i] != nullptr; i++)
|
||||
argc++;
|
||||
return make_cmd_shell_::make(
|
||||
argc ++;
|
||||
shell res = make_cmd_shell_::make(
|
||||
{}, argc, cmd,
|
||||
+[](int, char ** argv) {::free(argv);})
|
||||
kvm_close(kd);
|
||||
return res;
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
@@ -399,6 +392,8 @@ shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
kvm_close(kd);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
230
src/ext/env.cpp
230
src/ext/env.cpp
@@ -23,7 +23,6 @@
|
||||
#if (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <TargetConditionals.h>
|
||||
#if !TARGET_OS_IOS
|
||||
#include <algorithm>
|
||||
#include <sys/proc_info.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <libproc.h>
|
||||
@@ -35,52 +34,21 @@
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <kvm.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
#if defined(__DragonFly__)
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
#include <kvm.h>
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <kvm.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <libprocstat.h>
|
||||
#endif
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <kvm.h>
|
||||
#endif
|
||||
|
||||
#if defined(__sun)
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <kvm.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/proc.h>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
@@ -137,7 +105,7 @@ const environment::char_type * dereference(native_env_iterator iterator)
|
||||
return iterator;
|
||||
}
|
||||
|
||||
#elif (defined(__APPLE___) || defined(__MACH__)) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun)
|
||||
#elif (defined(__APPLE___) || defined(__MACH__)) || defined(__OpenBSD__)
|
||||
|
||||
void native_env_handle_deleter::operator()(native_env_handle_type h) const
|
||||
{
|
||||
@@ -162,6 +130,31 @@ const environment::char_type * dereference(native_env_iterator iterator)
|
||||
return iterator;
|
||||
}
|
||||
|
||||
|
||||
#elif defined(__FreeBSD__)
|
||||
|
||||
void native_env_handle_deleter::operator()(native_env_handle_type h) const
|
||||
{
|
||||
delete [] h;
|
||||
}
|
||||
|
||||
native_env_iterator next(native_env_iterator nh)
|
||||
{
|
||||
return ++nh ;
|
||||
}
|
||||
native_env_iterator find_end(native_env_iterator nh)
|
||||
{
|
||||
while (*nh != nullptr)
|
||||
nh++;
|
||||
|
||||
return nh ;
|
||||
}
|
||||
|
||||
const environment::char_type * dereference(native_env_iterator iterator)
|
||||
{
|
||||
return *iterator;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -345,90 +338,72 @@ env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
return ev;
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#elif defined(__FreeBSD__)
|
||||
env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
std::vector<char> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
const char *nlistf, *memf;
|
||||
nlistf = memf = "/dev/null";
|
||||
env_view ev;
|
||||
|
||||
struct closer
|
||||
unsigned cntp = 0;
|
||||
procstat *proc_stat = procstat_open_sysctl();
|
||||
if (proc_stat != nullptr)
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
kinfo_proc *proc_info = procstat_getprocs(proc_stat, KERN_PROC_PID, pid, &cntp);
|
||||
if (proc_info != nullptr)
|
||||
{
|
||||
kvm_close(kd);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)};
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); return {};}
|
||||
if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, &cntp))) {
|
||||
char **env = kvm_getenvv(kd.get(), proc_info, 0);
|
||||
if (env) {
|
||||
for (int i = 0; env[i] != nullptr; i++)
|
||||
char **env = procstat_getenvv(proc_stat, proc_info, 0);
|
||||
if (env != nullptr)
|
||||
{
|
||||
for (int j = 0; j < strlen(env[i]); j++)
|
||||
vec.push_back(env[i][j]);
|
||||
vec.push_back('\0');
|
||||
auto e = env;
|
||||
std::size_t n = 0u, len = 0u;
|
||||
while (e && *e != nullptr)
|
||||
{
|
||||
n ++;
|
||||
len += std::strlen(*e);
|
||||
e++;
|
||||
}
|
||||
std::size_t mem_needed =
|
||||
// environ - nullptr - strlen + null terminators
|
||||
(n * sizeof(char*)) + sizeof(char*) + len + n;
|
||||
|
||||
char * out = new (std::nothrow) char[mem_needed];
|
||||
if (out != nullptr)
|
||||
{
|
||||
auto eno = reinterpret_cast<char**>(out);
|
||||
auto eeo = eno;
|
||||
auto str = out + (n * sizeof(char*)) + sizeof(char*);
|
||||
e = env;
|
||||
while (*e != nullptr)
|
||||
{
|
||||
auto len = std::strlen(*e) + 1u;
|
||||
std::memcpy(str, *e, len);
|
||||
*eno = str;
|
||||
str += len;
|
||||
eno ++;
|
||||
e++;
|
||||
}
|
||||
*eno = nullptr;
|
||||
|
||||
ev.handle_.reset(eeo);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
}
|
||||
vec.push_back('\0');
|
||||
procstat_freeprocs(proc_stat, proc_info);
|
||||
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
procstat_close(proc_stat);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
env_view ev;
|
||||
ev.handle_.reset(new char[vec.size()]());
|
||||
std::copy(vec.begin(), vec.end(), ev.handle_.get());
|
||||
return ev;
|
||||
}
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
std::vector<char> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc2 *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
{
|
||||
kvm_close(kd);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)};
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); return {};}
|
||||
if ((proc_info = kvm_getproc2(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc2), &cntp))) {
|
||||
char **env = kvm_getenvv2(kd.get(), proc_info, 0);
|
||||
if (env) {
|
||||
for (int i = 0; env[i] != nullptr; i++)
|
||||
{
|
||||
for (int j = 0; j < strlen(env[i]); j++)
|
||||
vec.push_back(env[i][j]);
|
||||
vec.push_back('\0');
|
||||
}
|
||||
vec.push_back('\0');
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
env_view ev;
|
||||
ev.handle_.reset(new char[vec.size()]());
|
||||
std::copy(vec.begin(), vec.end(), ev.handle_.get());
|
||||
return ev;
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
|
||||
std::vector<char> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
@@ -442,7 +417,7 @@ env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)};
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); return {};}
|
||||
if (!kd.get()) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return {};}
|
||||
if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &cntp)))
|
||||
{
|
||||
char **env = kvm_getenvv(kd.get(), proc_info, 0);
|
||||
@@ -467,53 +442,6 @@ env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
std::copy(vec.begin(), vec.end(), ev.handle_.get());
|
||||
return ev;
|
||||
}
|
||||
#elif defined(__sun)
|
||||
env_view env(boost::process::v2::pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
std::vector<char> vec;
|
||||
char **env = nullptr;
|
||||
proc *proc_info = nullptr;
|
||||
user *proc_user = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
{
|
||||
kvm_close(kd);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr)};
|
||||
if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); return {};}
|
||||
if ((proc_info = kvm_getproc(kd.get(), pid)))
|
||||
{
|
||||
if ((proc_user = kvm_getu(kd.get(), proc_info)))
|
||||
{
|
||||
if (!kvm_getcmd(kd.get(), proc_info, proc_user, nullptr, &env))
|
||||
{
|
||||
for (int i = 0; env[i] != nullptr; i++)
|
||||
{
|
||||
for (int j = 0; j < strlen(env[i]); j++)
|
||||
vec.push_back(env[i][j]);
|
||||
vec.push_back('\0');
|
||||
}
|
||||
vec.push_back('\0');
|
||||
free(env);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
|
||||
env_view ev;
|
||||
ev.handle_.reset(new char[vec.size()]());
|
||||
std::copy(vec.begin(), vec.end(), ev.handle_.get());
|
||||
return ev;
|
||||
}
|
||||
#endif
|
||||
|
||||
env_view env(boost::process::v2::pid_type pid)
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
#if (defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__))
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
|
||||
#if !defined(__FreeBSD__)
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
90
src/pid.cpp
90
src/pid.cpp
@@ -29,11 +29,10 @@
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <fcntl.h>
|
||||
#include <kvm.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#include <libutil.h>
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
#if (defined(__DragonFly__) || defined(__OpenBSD__))
|
||||
@@ -278,29 +277,13 @@ std::vector<pid_type> all_pids(boost::system::error_code & ec)
|
||||
{
|
||||
std::vector<pid_type> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
const char *nlistf, *memf;
|
||||
nlistf = memf = "/dev/null";
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
{
|
||||
kvm_close(kd);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)};
|
||||
if (!kd)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return vec;
|
||||
}
|
||||
if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_ALL, 0, &cntp)))
|
||||
kinfo_proc *proc_info = kinfo_getallproc(&cntp);
|
||||
if (proc_info)
|
||||
{
|
||||
vec.reserve(cntp);
|
||||
for (int i = 0; i < cntp; i++)
|
||||
for (int i = 0; i < cntp; i++)
|
||||
vec.push_back(proc_info[i].ki_pid);
|
||||
free(proc_info);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
@@ -310,28 +293,11 @@ std::vector<pid_type> all_pids(boost::system::error_code & ec)
|
||||
pid_type parent_pid(pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
pid_type ppid = static_cast<pid_type>(-1);
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
const char *nlistf, *memf;
|
||||
nlistf = memf = "/dev/null";
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
{
|
||||
kvm_close(kd);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)};
|
||||
if (!kd)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return ppid;
|
||||
}
|
||||
if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, &cntp)))
|
||||
kinfo_proc *proc_info = kinfo_getproc(pid);
|
||||
if (proc_info)
|
||||
{
|
||||
ppid = proc_info->ki_ppid;
|
||||
free(proc_info);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
@@ -341,26 +307,9 @@ pid_type parent_pid(pid_type pid, boost::system::error_code & ec)
|
||||
std::vector<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
std::vector<pid_type> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
const char *nlistf, *memf;
|
||||
nlistf = memf = "/dev/null";
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
{
|
||||
kvm_close(kd);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)};
|
||||
if (!kd)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
return vec;
|
||||
}
|
||||
if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_ALL, 0, &cntp)))
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = kinfo_getallproc(&cntp);
|
||||
if (proc_info)
|
||||
{
|
||||
vec.reserve(cntp);
|
||||
for (int i = 0; i < cntp; i++)
|
||||
@@ -370,6 +319,7 @@ std::vector<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
|
||||
vec.push_back(proc_info[i].ki_pid);
|
||||
}
|
||||
}
|
||||
free(proc_info);
|
||||
}
|
||||
else
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
@@ -385,7 +335,6 @@ std::vector<pid_type> all_pids(boost::system::error_code & ec)
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
const char *nlistf, *memf;
|
||||
nlistf = memf = "/dev/null";
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -419,7 +368,6 @@ pid_type parent_pid(pid_type pid, boost::system::error_code & ec)
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
const char *nlistf, *memf;
|
||||
nlistf = memf = "/dev/null";
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -453,7 +401,6 @@ std::vector<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
const char *nlistf, *memf;
|
||||
nlistf = memf = "/dev/null";
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -491,7 +438,6 @@ std::vector<pid_type> all_pids(boost::system::error_code & ec)
|
||||
std::vector<pid_type> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc2 *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -524,7 +470,6 @@ pid_type parent_pid(pid_type pid, boost::system::error_code & ec)
|
||||
pid_type ppid = static_cast<pid_type>(-1);
|
||||
int cntp = 0;
|
||||
kinfo_proc2 *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -553,7 +498,6 @@ std::vector<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
|
||||
std::vector<pid_type> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc2 *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -591,7 +535,6 @@ std::vector<pid_type> all_pids(boost::system::error_code & ec)
|
||||
std::vector<pid_type> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -627,7 +570,6 @@ pid_type parent_pid(pid_type pid, boost::system::error_code & ec)
|
||||
pid_type ppid = static_cast<pid_type>(-1);
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -656,7 +598,6 @@ std::vector<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
|
||||
std::vector<pid_type> vec;
|
||||
int cntp = 0;
|
||||
kinfo_proc *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -695,7 +636,6 @@ std::vector<pid_type> all_pids(boost::system::error_code & ec)
|
||||
std::vector<pid_type> vec;
|
||||
struct pid cur_pid;
|
||||
proc *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -729,7 +669,6 @@ pid_type parent_pid(pid_type pid, boost::system::error_code & ec)
|
||||
{
|
||||
pid_type ppid = static_cast<pid_type>(-1);
|
||||
proc *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
@@ -758,7 +697,6 @@ std::vector<pid_type> child_pids(pid_type pid, boost::system::error_code & ec)
|
||||
std::vector<pid_type> vec;
|
||||
struct pid cur_pid;
|
||||
proc *proc_info = nullptr;
|
||||
|
||||
struct closer
|
||||
{
|
||||
void operator()(kvm_t * kd)
|
||||
|
||||
@@ -36,4 +36,14 @@ function(boost_process_v2_test_with_target name)
|
||||
endfunction()
|
||||
|
||||
boost_process_v2_test_with_target(process)
|
||||
boost_process_v2_test_with_target(ext)
|
||||
boost_process_v2_test_with_target(ext)
|
||||
|
||||
|
||||
add_executable(boost_process_v2_test_experimental_pty_target experimental/pty_target.cpp)
|
||||
target_link_libraries(boost_process_v2_test_experimental_pty_target PUBLIC Boost::process Boost::system)
|
||||
|
||||
add_executable(boost_process_v2_test_experimental_pty experimental/pty.cpp)
|
||||
target_link_libraries(boost_process_v2_test_experimental_pty Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
add_dependencies(boost_process_v2_test_experimental_pty boost_process_v2_test_experimental_pty_target)
|
||||
add_test(NAME boost_process_v2_test_experimental_pty COMMAND $<TARGET_FILE:boost_process_v2_test_experimental_pty>
|
||||
-- $<TARGET_FILE:boost_process_v2_test_experimental_pty_target>)
|
||||
@@ -21,7 +21,9 @@ project : requirements
|
||||
<target-os>windows:<define>_WIN32_WINNT=0x0601
|
||||
<target-os>linux:<linkflags>-lpthread
|
||||
<target-os>freebsd:<linkflags>-lpthread
|
||||
<target-os>freebsd:<linkflags>-lutil
|
||||
<target-os>freebsd:<linkflags>-lkvm
|
||||
<target-os>freebsd:<linkflags>-lprocstat
|
||||
<target-os>bsd:<linkflags>-lpthread
|
||||
<target-os>bsd:<linkflags>-lkvm
|
||||
<target-os>netbsd:<linkflags>-lpthread
|
||||
@@ -61,3 +63,11 @@ test-suite with_target :
|
||||
[ run ext.cpp $(test_impl) : --log_level=all --catch_system_errors=no -- : target : <target-os>darwin:<build>no ]
|
||||
;
|
||||
|
||||
exe experimental_pty_target : experimental/pty_target.cpp :
|
||||
<warnings>off <target-os>windows:<source>shell32
|
||||
<target-os>windows:<source>Ntdll
|
||||
;
|
||||
|
||||
test-suite experimental :
|
||||
[ run experimental/pty.cpp $(test_impl) : --log_level=all --catch_system_errors=no -- : experimental_pty_target ]
|
||||
;
|
||||
|
||||
364
test/v2/experimental/pty.cpp
Normal file
364
test/v2/experimental/pty.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
//
|
||||
// Copyright (c) 2024 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)
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
#include <boost/process/v2/experimental/pty.hpp>
|
||||
#include <boost/process/v2/process.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/read_until.hpp>
|
||||
|
||||
#include <boost/asio/yield.hpp>
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_WINDOWS) || (NTDDI_VERSION >= 0x0A000006)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sync_plain)
|
||||
{
|
||||
namespace bp2 = boost::process::v2;
|
||||
namespace net = boost::process::v2::net;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bp2::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
boost::asio::io_context ctx;
|
||||
bp2::experimental::pty pt{ctx};
|
||||
bp2::process proc(ctx, pth, {}, pt);
|
||||
|
||||
|
||||
char buf[4];
|
||||
net::read(pt, net::buffer(buf));
|
||||
|
||||
std::string l1 = "Hello", l2 = ", ", l3 = "World!", l4 = "\n";
|
||||
|
||||
std::string read_buffer;
|
||||
read_buffer.resize(128);
|
||||
auto rb = net::buffer(read_buffer);
|
||||
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l1)), 5);
|
||||
auto n = pt.read_some(rb);
|
||||
BOOST_CHECK_MESSAGE(n == 5, read_buffer.substr(0, n)); rb += n;
|
||||
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l2)), 2);
|
||||
BOOST_CHECK_EQUAL(n = pt.read_some(rb), 2); rb += n;
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l3)), 6);
|
||||
BOOST_CHECK_EQUAL(n = pt.read_some(rb), 6); rb += n;
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l4)), 1);
|
||||
BOOST_CHECK_EQUAL(n = pt.read_some(rb), 2); rb += n;
|
||||
|
||||
n = read_buffer.size() - rb.size();
|
||||
|
||||
BOOST_CHECK_EQUAL(read_buffer.substr(0, n), "HELLO, WORLD!\r\n");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_plain)
|
||||
{
|
||||
namespace bp2 = boost::process::v2;
|
||||
namespace net = boost::process::v2::net;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bp2::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
boost::asio::io_context ctx;
|
||||
bp2::experimental::pty pt{ctx};
|
||||
bp2::process proc(ctx, pth, {"--async"}, pt);
|
||||
|
||||
|
||||
char buf[4];
|
||||
net::read(pt, net::buffer(buf));
|
||||
|
||||
|
||||
struct op : net::coroutine
|
||||
{
|
||||
bp2::experimental::pty &pt;
|
||||
bp2::process &proc;
|
||||
|
||||
op(bp2::experimental::pty &pt, bp2::process &proc) : pt(pt), proc(proc) {}
|
||||
|
||||
std::string read_buffer = std::string(64, ' ');
|
||||
net::mutable_buffer rb = net::buffer(read_buffer);
|
||||
std::string l1 = "Hello", l2 = ", ", l3 = "World!", l4 = "\n";
|
||||
|
||||
|
||||
void operator()(bp2::error_code ec, std::size_t n)
|
||||
{
|
||||
BOOST_REQUIRE_MESSAGE(!ec, ec.message());
|
||||
reenter(this)
|
||||
{
|
||||
yield pt.async_write_some(net::buffer(l1), *this);
|
||||
BOOST_CHECK_EQUAL(n, 5);
|
||||
yield pt.async_read_some(rb, *this);
|
||||
BOOST_CHECK_EQUAL(n, 5); rb += n;
|
||||
|
||||
yield pt.async_write_some(net::buffer(l2), *this);
|
||||
BOOST_CHECK_EQUAL(n, 2);
|
||||
yield pt.async_read_some(rb, *this);
|
||||
BOOST_CHECK_EQUAL(n, 2);
|
||||
rb += n;
|
||||
yield pt.async_write_some(net::buffer(l3), *this);
|
||||
BOOST_CHECK_EQUAL(n, 6);
|
||||
yield pt.async_read_some(rb, *this);
|
||||
BOOST_CHECK_EQUAL(n, 6); rb += n;
|
||||
yield pt.async_write_some(net::buffer(l4), *this);
|
||||
BOOST_CHECK_EQUAL(n, 1);
|
||||
yield pt.async_read_some(rb, *this);
|
||||
BOOST_CHECK_EQUAL(n, 2); rb += n;
|
||||
|
||||
n = read_buffer.size() - rb.size();
|
||||
BOOST_CHECK_EQUAL(read_buffer.substr(0, n), "HELLO, WORLD!\r\n");
|
||||
|
||||
proc.terminate();
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
op{pt, proc}({}, 0u);
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sync_echo)
|
||||
{
|
||||
namespace bp2 = boost::process::v2;
|
||||
namespace net = boost::process::v2::net;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bp2::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
boost::asio::io_context ctx;
|
||||
bp2::experimental::pty pt{ctx};
|
||||
bp2::process proc(ctx, pth, {"--echo"}, pt);
|
||||
|
||||
|
||||
char buf[4];
|
||||
net::read(pt, net::buffer(buf));
|
||||
|
||||
std::string l1 = "Hello", l2 = ", ", l3 = "World!", l4 = "\n";
|
||||
|
||||
std::string read_buffer;
|
||||
read_buffer.resize(64);
|
||||
auto rb = net::buffer(read_buffer);
|
||||
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l1)), 5);
|
||||
|
||||
auto n = net::read(pt, net::buffer(rb, 10));
|
||||
BOOST_CHECK_EQUAL(n, 10);
|
||||
rb += n;
|
||||
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l2)), 2);
|
||||
BOOST_CHECK_EQUAL(n = net::read(pt, net::buffer(rb, 4)), 4); rb += n;
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l3)), 6);
|
||||
BOOST_CHECK_EQUAL(n = net::read(pt, net::buffer(rb, 12)), 12); rb += n;
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l4)), 1);
|
||||
BOOST_CHECK_EQUAL(n = net::read(pt, net::buffer(rb, 4)), 4); rb += n;
|
||||
|
||||
n = read_buffer.size() - rb.size();
|
||||
|
||||
BOOST_CHECK_EQUAL(read_buffer.substr(0, n), "HelloHELLO, , World!WORLD!^J\r\n");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_echo)
|
||||
{
|
||||
namespace bp2 = boost::process::v2;
|
||||
namespace net = boost::process::v2::net;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bp2::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
boost::asio::io_context ctx;
|
||||
bp2::experimental::pty pt{ctx};
|
||||
bp2::process proc(ctx, pth, {"--async", "--echo"}, pt);
|
||||
|
||||
char buf[4];
|
||||
net::read(pt, net::buffer(buf));
|
||||
|
||||
|
||||
struct op : net::coroutine
|
||||
{
|
||||
bp2::experimental::pty &pt;
|
||||
bp2::process &proc;
|
||||
|
||||
op(bp2::experimental::pty &pt, bp2::process &proc) : pt(pt), proc(proc) {}
|
||||
|
||||
std::string read_buffer = std::string(64, ' ');
|
||||
net::mutable_buffer rb = net::buffer(read_buffer);
|
||||
std::string l1 = "Hello", l2 = ", ", l3 = "World!", l4 = "\n";
|
||||
|
||||
|
||||
void operator()(bp2::error_code ec, std::size_t n)
|
||||
{
|
||||
BOOST_REQUIRE_MESSAGE(!ec, ec.message());
|
||||
reenter(this)
|
||||
{
|
||||
yield pt.async_write_some(net::buffer(l1), *this);
|
||||
BOOST_CHECK_EQUAL(n, 5);
|
||||
yield net::async_read(pt, net::buffer(rb, 10), *this);
|
||||
BOOST_CHECK_EQUAL(n, 10); rb += n;
|
||||
|
||||
yield pt.async_write_some(net::buffer(l2), *this);
|
||||
BOOST_CHECK_EQUAL(n, 2);
|
||||
yield net::async_read(pt, net::buffer(rb, 4), *this);
|
||||
BOOST_CHECK_EQUAL(n, 4);
|
||||
rb += n;
|
||||
yield pt.async_write_some(net::buffer(l3), *this);
|
||||
BOOST_CHECK_EQUAL(n, 6);
|
||||
yield net::async_read(pt, net::buffer(rb, 12), *this);
|
||||
BOOST_CHECK_EQUAL(n, 12); rb += n;
|
||||
yield pt.async_write_some(net::buffer(l4), *this);
|
||||
BOOST_CHECK_EQUAL(n, 1);
|
||||
yield net::async_read(pt, net::buffer(rb, 4), *this);
|
||||
BOOST_CHECK_EQUAL(n, 4); rb += n;
|
||||
|
||||
n = read_buffer.size() - rb.size();
|
||||
BOOST_CHECK_EQUAL(read_buffer.substr(0, n), "HelloHELLO, , World!WORLD!^J\r\n");
|
||||
|
||||
proc.terminate();
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
op{pt, proc}({}, 0u);
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_line)
|
||||
{
|
||||
namespace bp2 = boost::process::v2;
|
||||
namespace net = boost::process::v2::net;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bp2::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
boost::asio::io_context ctx;
|
||||
bp2::experimental::pty pt{ctx};
|
||||
bp2::process proc(ctx, pth, {"--async", "--line"}, pt);
|
||||
|
||||
|
||||
char buf[4];
|
||||
net::read(pt, net::buffer(buf));
|
||||
|
||||
|
||||
std::size_t read = 0u;
|
||||
std::string read_buffer;
|
||||
net::async_read_until(
|
||||
pt, net::dynamic_buffer(read_buffer), '\n',
|
||||
[&](bp2::error_code ec, std::size_t n)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(read_buffer.substr(0, n), "HELLO, WORLD!\r\n");
|
||||
read = n;
|
||||
});
|
||||
|
||||
struct op : net::coroutine
|
||||
{
|
||||
bp2::experimental::pty &pt;
|
||||
std::size_t & read;
|
||||
|
||||
op(bp2::experimental::pty &pt, std::size_t & read) : pt(pt), read(read) {}
|
||||
|
||||
std::string l1 = "Hello", l2 = ", ", l3 = "World!", l4 = "\n";
|
||||
|
||||
|
||||
void operator()(bp2::error_code ec, std::size_t n)
|
||||
{
|
||||
BOOST_REQUIRE_MESSAGE(!ec, ec.message());
|
||||
reenter(this)
|
||||
{
|
||||
yield pt.async_write_some(net::buffer(l1), *this);
|
||||
BOOST_CHECK_EQUAL(n, 5);
|
||||
BOOST_CHECK_EQUAL(read, 0);
|
||||
yield pt.async_write_some(net::buffer(l2), *this);
|
||||
BOOST_CHECK_EQUAL(n, 2);
|
||||
BOOST_CHECK_EQUAL(read, 0);
|
||||
yield pt.async_write_some(net::buffer(l3), *this);
|
||||
BOOST_CHECK_EQUAL(n, 6);
|
||||
BOOST_CHECK_EQUAL(read, 0);
|
||||
yield pt.async_write_some(net::buffer(l4), *this);
|
||||
BOOST_CHECK_EQUAL(n, 1);
|
||||
BOOST_CHECK_EQUAL(read, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
op{pt, read}({}, 0u);
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sync_line_echo)
|
||||
{
|
||||
namespace bp2 = boost::process::v2;
|
||||
namespace net = boost::process::v2::net;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bp2::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
boost::asio::io_context ctx;
|
||||
bp2::experimental::pty pt{ctx};
|
||||
bp2::process proc(ctx, pth, {"--line", "--echo"}, pt);
|
||||
|
||||
|
||||
char buf[4];
|
||||
net::read(pt, net::buffer(buf));
|
||||
|
||||
std::string l1 = "Hello", l2 = ", ", l3 = "World!", l4 = "\n";
|
||||
|
||||
std::string read_buffer;
|
||||
read_buffer.resize(64);
|
||||
auto rb = net::buffer(read_buffer);
|
||||
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l1)), 5);
|
||||
auto n = pt.read_some(rb);
|
||||
BOOST_CHECK_EQUAL(n, 5); rb += n;
|
||||
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l2)), 2);
|
||||
BOOST_CHECK_EQUAL(n = pt.read_some(rb), 2); rb += n;
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l3)), 6);
|
||||
BOOST_CHECK_EQUAL(n = pt.read_some(rb), 6); rb += n;
|
||||
BOOST_CHECK_EQUAL(pt.write_some(net::buffer(l4)), 1);
|
||||
BOOST_CHECK_EQUAL(n = pt.read_some(rb), 2); rb += n;
|
||||
|
||||
|
||||
rb += pt.read_some(rb);
|
||||
n = read_buffer.size() - rb.size();
|
||||
|
||||
BOOST_CHECK_EQUAL(read_buffer.substr(0, n), "Hello, World!\r\nHELLO, WORLD!\r\n");
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(resize)
|
||||
{
|
||||
namespace bp2 = boost::process::v2;
|
||||
namespace net = boost::process::v2::net;
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = bp2::filesystem::absolute(master_test_suite().argv[1]);
|
||||
|
||||
boost::asio::io_context ctx;
|
||||
bp2::experimental::pty pt{ctx};
|
||||
|
||||
bp2::experimental::console_size_t cs{100, 40};
|
||||
pt.open(cs);
|
||||
|
||||
bp2::process proc(ctx, pth, {"--wait-resize"}, pt);
|
||||
|
||||
|
||||
char buf[4];
|
||||
net::read(pt, net::buffer(buf));
|
||||
|
||||
std::string line;
|
||||
auto n = net::read_until(pt, net::dynamic_buffer(line), '\n');
|
||||
BOOST_CHECK_EQUAL(line, "cols=100 rows=40\r\n");
|
||||
|
||||
line.clear();
|
||||
|
||||
cs.columns = 85;
|
||||
cs.rows = 35;
|
||||
pt.resize(cs);
|
||||
|
||||
n = net::read_until(pt, net::dynamic_buffer(line), '\n');
|
||||
BOOST_CHECK_EQUAL(line, "cols=85 rows=35\r\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
119
test/v2/experimental/pty_target.cpp
Normal file
119
test/v2/experimental/pty_target.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// Copyright (c) 2024 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)
|
||||
//
|
||||
|
||||
#include <boost/process/v2/experimental/stream.hpp>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/asio/yield.hpp>
|
||||
|
||||
|
||||
namespace net = boost::asio;
|
||||
namespace bp = boost::process::v2;
|
||||
|
||||
char buf[512];
|
||||
|
||||
bool async_check = false;
|
||||
|
||||
struct async_op : net::coroutine
|
||||
{
|
||||
bp::experimental::basic_stream<net::io_context::executor_type> &in, &out;
|
||||
|
||||
async_op(bp::experimental::basic_stream<net::io_context::executor_type> &in,
|
||||
bp::experimental::basic_stream<net::io_context::executor_type> &out) : in(in), out(out) {}
|
||||
void operator()(bp::error_code ec = {}, std::size_t n = {})
|
||||
{
|
||||
if (ec)
|
||||
return;
|
||||
reenter(this)
|
||||
{
|
||||
while (in.is_open())
|
||||
{
|
||||
async_check = false;
|
||||
net::post(in.get_executor(), []{async_check=true;});
|
||||
yield in.async_read_some(net::buffer(buf), *this);
|
||||
assert(async_check);
|
||||
boost::algorithm::to_upper(buf);
|
||||
yield net::async_write(out, net::buffer(buf, n), *this);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
try
|
||||
{
|
||||
net::io_context ctx;
|
||||
|
||||
auto in = bp::experimental::open_stdin(ctx.get_executor());
|
||||
assert(in.is_pty());
|
||||
//assert(in.echo());
|
||||
//assert(in.line());
|
||||
|
||||
std::vector<std::string> args{argv + 1, argv + argc};
|
||||
|
||||
const bool async = std::find(args.begin(), args.end(), "--async") != args.end();
|
||||
const bool stder = std::find(args.begin(), args.end(), "--stderr") != args.end();
|
||||
const bool echo = std::find(args.begin(), args.end(), "--echo") != args.end();
|
||||
const bool line = std::find(args.begin(), args.end(), "--line") != args.end();
|
||||
const bool wait = std::find(args.begin(), args.end(), "--wait-resize") != args.end();
|
||||
|
||||
auto out = stder ? bp::experimental::open_stderr(ctx.get_executor())
|
||||
: bp::experimental::open_stdout(ctx.get_executor());
|
||||
in.set_echo(echo);
|
||||
in.set_line(line);
|
||||
|
||||
assert(in.echo() == echo);
|
||||
assert(in.line() == line);
|
||||
|
||||
net::write(out, net::buffer("sync", 4));
|
||||
|
||||
if (wait)
|
||||
{
|
||||
|
||||
in.async_wait_for_size_change(
|
||||
[&](bp::error_code ec, bp::experimental::console_size_t cs)
|
||||
{
|
||||
assert(!ec);
|
||||
printf("cols=%d rows=%d\n", cs.columns, cs.rows);
|
||||
});
|
||||
|
||||
net::post(ctx, [&]{
|
||||
const auto sz = in.console_size();
|
||||
printf("cols=%d rows=%d\n", sz.columns, sz.rows);
|
||||
});
|
||||
ctx.run();
|
||||
}
|
||||
else if (async)
|
||||
{
|
||||
net::post(ctx, async_op{in, out});
|
||||
ctx.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[512];
|
||||
auto f = fopen("./test.txt", "w");
|
||||
while (in.is_open())
|
||||
{
|
||||
auto n = in.read_some(net::buffer(buf));
|
||||
boost::algorithm::to_upper(buf);
|
||||
fwrite(buf, 1u, n, f);
|
||||
fflush(f);
|
||||
net::write(out, net::buffer(buf, n));
|
||||
}
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
catch(boost::system::system_error & se)
|
||||
{
|
||||
fprintf(stderr, "Pty-Target exception: %s(%d): %s\n", se.code().location().file_name(), se.code().location().line(), se.what());
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user