2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-19 04:22:15 +00:00

pty windows build fixes.

This commit is contained in:
Klemens Morgenstern
2024-10-29 14:54:56 +08:00
parent e4070119ce
commit 626dd5f1bc
5 changed files with 158 additions and 134 deletions

View File

@@ -58,7 +58,7 @@ class basic_pty
template<typename Arg>
handle_t_(Arg && arg) : rm{arg} {}
bool is_open() {return rm.is_open();}
bool is_open() const {return rm.is_open();}
};
handle_t_ handle_;
@@ -600,10 +600,10 @@ class basic_pty
this->close(ec);
}
#else
net::connect_pipe(handle_.rm, handle_.ws, ec);
net::connect_pipe(handle_.rm, handle_.ws, ec); // output
if (!ec)
{
net::connect_pipe(handle_.wm, handle_.rs, ec);
net::connect_pipe(handle_.rs, handle_.wm, ec); // input
if (ec)
handle_.rm.close(ec);
}
@@ -698,10 +698,14 @@ class basic_pty
{
error_code ec;
if (!is_open())
open(ec);
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);

View File

@@ -207,7 +207,7 @@ struct basic_stream
}
/// Get the executor associated with the object.
const executor_type& get_executor() noexcept
executor_type get_executor() noexcept
{
return handle_.get_executor();
}
@@ -390,7 +390,7 @@ struct basic_stream
struct ::termios t;
return !tcgetattr(handle_.native_handle(), &t);
#else
return handle_.index() == 0u;
return handle_.object.is_open();
#endif
}
@@ -421,7 +421,7 @@ struct basic_stream
else
mode &= ~ENABLE_ECHO_INPUT;
if (!ec && SetConsoleMode(handle_.native_handle(), mode))
if (!ec && !SetConsoleMode(handle_.native_handle(), mode))
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
#endif
}
@@ -484,11 +484,11 @@ struct basic_stream
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
if (enable)
mode |= ENABLE_LINE_INPUT;
mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT ;
else
mode &= ~ENABLE_LINE_INPUT;
mode &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT );
if (!ec && SetConsoleMode(handle_.native_handle(), mode))
if (!ec && !SetConsoleMode(handle_.native_handle(), mode))
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
#endif
}
@@ -561,24 +561,26 @@ struct basic_stream
{
if (!this_->is_pty())
{
auto e = net::get_associated_immediate_executor(self, this_->sig_winch_.get_executor());
auto e = net::get_associated_immediate_executor(self, this_->handle_.object.get_executor());
return net::dispatch(e, net::append(std::move(self), net::error::operation_not_supported));
}
buf = this_->handle_.cs_buf_;
this_->handle_.trigger_size_.async_wait(std::move(self));
this_->async_wait(std::move(self));
}
template<typename Self>
void operator()(Self && self, error_code ec)
{
if (ec == net::error::operation_aborted
&& !self.get_cancellation_state().cancelled()
&& buf != this_->handle_.cs_buf_)
ec.clear();
if (this_->handle_.trigger_size_.expiry() == std::chrono::steady_clock::time_point::min())
BOOST_PROCESS_V2_ASSIGN_EC(ec, net::error::broken_pipe);
if (!ec)
{
CONSOLE_SCREEN_BUFFER_INFO bi;
if (::GetConsoleScreenBufferInfo(this_->handle_.object.native_handle(), &bi))
{
this_->handle_.cs_buf_.columns = bi.dwSize.X;
this_->handle_.cs_buf_.rows = bi.dwSize.Y;
}
else
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
}
self.complete(ec, this_->handle_.cs_buf_);
}
@@ -600,7 +602,7 @@ struct basic_stream
async_wait_for_size_change_op_{this}, handler, handle_))
{
return net::async_compose<WaitHandler, void (error_code, console_size_t)>(
async_wait_for_size_change_op_{this}, handler, get_executor());
async_wait_for_size_change_op_{this}, handler, handle_);
}
/// Wait for the stream to become ready to read.
@@ -710,9 +712,9 @@ struct basic_stream
auto async_wait(WaitToken&& token = net::default_completion_token_t<executor_type>())
-> decltype(this->handle_.async_wait(
#if defined(BOOST_PROCESS_V2_POSIX)
net::posix::descriptor_base::wait_read, std::declval<WaitToken&&>()
net::posix::descriptor_base::wait_read,
#endif
))
std::declval<WaitToken&&>()))
{
return this->handle_.async_wait(
#if defined(BOOST_PROCESS_V2_POSIX)
@@ -836,11 +838,11 @@ struct basic_stream
* @li @c cancellation_type::total
*/
template <typename MutableBufferSequence,
BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void (error_code, std::size_t)) ReadToken =
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,
ReadToken&& token = net::default_completion_token_t<executor_type>())
-> decltype(this->handle_.async_read_some(buffers, std::forward<ReadToken>(token)))
-> decltype(this->handle_.async_read_some(buffers, std::forward<ReadToken>(token)))
{
return this->handle_.async_read_some(buffers, std::forward<ReadToken>(token));
}
@@ -975,11 +977,11 @@ basic_stream<Executor> open_stdin(Executor exec, bool duplicate, error_code & ec
typename basic_stream<Executor>::native_handle_type handle;
#if defined(BOOST_WINDOWS_API)
if (!duplicate)
handle = GetStdHandle(STDIN_FILENO);
handle = GetStdHandle(STD_INPUT_HANDLE);
else if (
!DuplicateHandle(
GetCurrentProcess(),
GetStdHandle(STDIN_FILENO),
GetStdHandle(STD_INPUT_HANDLE),
GetCurrentProcess(),
&handle,
0u,
@@ -1000,11 +1002,11 @@ basic_stream<Executor> open_stdout(Executor exec, bool duplicate, error_code & e
typename basic_stream<Executor>::native_handle_type handle;
#if defined(BOOST_WINDOWS_API)
if (!duplicate)
handle = GetStdHandle(STDOUT_FILENO);
handle = GetStdHandle(STD_OUTPUT_HANDLE);
else if (
!DuplicateHandle(
GetCurrentProcess(),
GetStdHandle(STDOUT_FILENO),
GetStdHandle(STD_OUTPUT_HANDLE),
GetCurrentProcess(),
&handle,
0u,
@@ -1025,11 +1027,11 @@ basic_stream<Executor> open_stderr(Executor exec, bool duplicate, error_code & e
typename basic_stream<Executor>::native_handle_type handle;
#if defined(BOOST_WINDOWS_API)
if (!duplicate)
handle = GetStdHandle(STDERR_FILENO);
handle = GetStdHandle(STD_ERROR_HANDLE);
else if (
!DuplicateHandle(
GetCurrentProcess(),
GetStdHandle(STDERR_FILENO),
GetStdHandle(STD_ERROR_HANDLE),
GetCurrentProcess(),
&handle,
0u,

View File

@@ -43,7 +43,6 @@ struct basic_stream_handle
{
net::windows::basic_object_handle<Executor> object;
net::windows::basic_stream_handle<Executor> stream;
net::steady_timer trigger_size_{get_executor(), std::chrono::steady_clock::time_point::max()};
v2::experimental::console_size_t cs_buf_{0u, 0u};
template<typename Arg>
@@ -51,15 +50,17 @@ struct basic_stream_handle
template<typename Executor1>
basic_stream_handle(basic_stream_handle<Executor1> &&lhs)
: object(std::move(lhs.object)), stream(std::move(lhs.stream)), trigger_size_(std::move(lhs.trigger_size_)),
cs_buf_(lhs.cs_buf_) {}
: object(std::move(lhs.object)), stream(std::move(lhs.stream)), cs_buf_(lhs.cs_buf_) {}
Executor get_executor() noexcept { return object.get_executor(); }
using executor_type = Executor;
executor_type get_executor() noexcept { return object.get_executor(); }
void assign(HANDLE h, error_code &ec)
{
if (GetFileType(h) == FILE_TYPE_CHAR) {
assert(GetFileType(h) == FILE_TYPE_CHAR);
if (GetFileType(h) == FILE_TYPE_CHAR)
{
stream.close(ec);
object.assign(h, ec);
DWORD flags;
@@ -68,15 +69,25 @@ struct basic_stream_handle
return;
}
flags |= ENABLE_VIRTUAL_TERMINAL_INPUT;
if (!::SetConsoleMode(h, flags) ||
!::SetConsoleCP(CP_UTF8) ||
!::SetConsoleOutputCP(CP_UTF8))
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);
trigger_size_.expires_at(std::chrono::steady_clock::time_point::max());
} else {
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);
}
@@ -94,14 +105,12 @@ struct basic_stream_handle
{
object.close(ec);
stream.close(ec);
trigger_size_.expires_at(std::chrono::steady_clock::time_point::min());
}
void close()
{
object.close();
stream.close();
trigger_size_.expires_at(std::chrono::steady_clock::time_point::min());
}
HANDLE native_handle()
@@ -178,10 +187,13 @@ struct basic_stream_handle
if (stream.is_open())
return stream.read_some(mbs, ec);
net::mutable_buffer buf;
asio::mutable_buffer buf;
for (auto itr = net::buffer_sequence_begin(mbs);
itr != net::buffer_sequence_end(mbs); itr++)
if (itr->size() > 0) {
if (itr->size() > 0)
{
buf = *itr;
break;
}
@@ -189,43 +201,16 @@ struct basic_stream_handle
if (buf.size() == 0u)
return 0u;
try_again:
INPUT_RECORD in_buffer[8092];
DWORD sz = buf.size(), read_size = 0u; //
if (!PeekConsoleInputW(object.native_handle(), in_buffer, sz, &read_size)) {
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
return 0u;
}
auto begin = in_buffer,
end = in_buffer + read_size;
DWORD keys = 0u;
for (auto itr = begin; itr != end; itr++) {
if (itr->EventType == KEY_EVENT
&& itr->Event.KeyEvent.bKeyDown)
keys += itr->Event.KeyEvent.wRepeatCount;
// not handling resize here: don't mix sync & async apis.
}
if (keys == 0u) {
// read all we just peeked
if (!::ReadConsoleW(object.native_handle(), in_buffer, read_size, sz)) {
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
return 0u;
}
goto try_again; // never read zero
}
read_size = 0u;
if (!ReadFile(object.native_handle(), buf.data(), keys, &read_size, NULL)) {
DWORD read_size = 0u;
if (!::ReadFile(object.native_handle(), buf.data(), buf.size(), &read_size, NULL))
{
const DWORD last_error = ::GetLastError();
if (ERROR_MORE_DATA == last_error)
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);
}
@@ -239,7 +224,7 @@ struct basic_stream_handle
void operator()(Self &&self)
{
if (this_->stream.is_open())
return this_->stream.async_reasd_some(buffer, std::move(self));
return this_->stream.async_read_some(buffer, std::move(self));
this_->async_wait(std::move(self));
}
@@ -247,10 +232,12 @@ struct basic_stream_handle
template<typename Self>
void operator()(Self &&self, error_code ec)
{
net::mutable_buffer buf;
asio::mutable_buffer buf;
for (auto itr = net::buffer_sequence_begin(buffer);
itr != net::buffer_sequence_end(buffer); itr++)
if (itr->size() > 0) {
if (itr->size() > 0)
{
buf = *itr;
break;
}
@@ -258,50 +245,56 @@ struct basic_stream_handle
if (buf.size() == 0u)
return self.complete(error_code{}, 0u);
WINDOW_BUFFER_SIZE_RECORD *window_change = nullptr;
try_again:
INPUT_RECORD in_buffer[8092];
DWORD sz = buf.size(), read_size = 0u; //
if (!PeekConsoleInputW(this_->object.native_handle(), in_buffer, sz, &read_size)) {
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;
end = in_buffer + read_size;
DWORD keys = 0u;
for (auto itr = begin; itr != end; itr++) {
if (itr->EventType == KEY_EVENT
&& itr->Event.KeyEvent.bKeyDown)
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;
else if (itr->EventType == WINDOW_BUFFER_SIZE_EVENT)
window_change = &itr->Event.WindowBufferSizeEvent;
has_cr |= itr->Event.KeyEvent.uChar.AsciiChar == '\r';
}
}
if (keys == 0u) {
// read all we just peeked
if (!::ReadConsoleW(this_->object.native_handle(), in_buffer, read_size, sz)) {
if (keys == 0u)
{
if (!::FlushConsoleInputBuffer(this_->object.native_handle()))
{
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
return self.complete(ec, 0u);
}
goto try_again; // never read zero
else
return this_->async_wait(std::move(self));
}
if (window_change) //
if (!has_cr)
{
this_->cs_buf_.columns = window_change->dwSize.X;
this_->cs_buf_.rows = window_change->dwSize.Y;
this_->trigger_size_.cancel();
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(), keys, &read_size, NULL)) {
if (!ReadFile(this_->object.native_handle(), buf.data(), to_read, &read_size, NULL))
{
const DWORD last_error = ::GetLastError();
if (ERROR_MORE_DATA == last_error)
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());
@@ -311,22 +304,22 @@ struct basic_stream_handle
}
template<typename Self>
void operator()(Self &&self, error_code ec, std::size_t n)
void operator()(Self &&self, error_code ec, std::size_t n_)
{
self.complete(ec, n);
self.complete(ec, n_);
}
};
template<typename MutableBuffer, typename CompletionToken>
auto async_read_some(const MutableBuffer &buffer, CompletionToken &&token)
-> decltype(boost::asio::async_initiate<CompletionToken,
void(boost::system::error_code, std::size_t)>(
boost::asio::composed(async_read_some_op<MutableBuffer>{this, buffer}, socket), token))
{
return boost::asio::async_initiate<CompletionToken,
-> decltype(net::async_initiate<CompletionToken,
void(boost::system::error_code, std::size_t)>(
boost::asio::composed(async_read_some_op<MutableBuffer>{this, buffer}, socket), token);
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>
@@ -335,10 +328,11 @@ struct basic_stream_handle
if (stream.is_open())
return stream.write_some(cbs, ec);
net::mutable_buffer buf;
net::const_buffer buf;
for (auto itr = net::buffer_sequence_begin(cbs);
itr != net::buffer_sequence_end(cbs); itr++)
if (itr->size() > 0) {
if (itr->size() > 0)
{
buf = *itr;
break;
}
@@ -347,9 +341,8 @@ struct basic_stream_handle
return 0u;
// get one screen size, that's how much we'll write
CONSOLE_SCREEN_BUFFER_INFOEX bi;
if (!GetConsoleScreenBufferInfoEx(object.native_handle(), &bi)) {
CONSOLE_SCREEN_BUFFER_INFO bi;
if (!GetConsoleScreenBufferInfo(object.native_handle(), &bi)) {
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
return 0u;
}
@@ -378,11 +371,23 @@ struct basic_stream_handle
error_code ec;
auto n = this_->write_some(buffer, ec);
auto exec = net::get_associated_immediate_executor(
handler, this_->sig_winch_.get_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);
}
};
}

View File

@@ -6,6 +6,8 @@
//
#include <boost/process/v2/experimental/pty.hpp>
#include <boost/process/v2/process.hpp>
@@ -37,12 +39,12 @@ BOOST_AUTO_TEST_CASE(sync_plain)
std::string l1 = "Hello", l2 = ", ", l3 = "World!", l4 = "\n";
std::string read_buffer;
read_buffer.resize(64);
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_EQUAL(n, 5); rb += n;
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;
@@ -77,6 +79,8 @@ BOOST_AUTO_TEST_CASE(async_plain)
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";
@@ -115,7 +119,7 @@ BOOST_AUTO_TEST_CASE(async_plain)
}
};
op{{}, pt, proc}({}, 0u);
op{pt, proc}({}, 0u);
ctx.run();
}
@@ -169,7 +173,6 @@ BOOST_AUTO_TEST_CASE(async_echo)
bp2::experimental::pty pt{ctx};
bp2::process proc(ctx, pth, {"--async", "--echo"}, pt);
char buf[4];
net::read(pt, net::buffer(buf));
@@ -179,6 +182,8 @@ BOOST_AUTO_TEST_CASE(async_echo)
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";
@@ -217,7 +222,7 @@ BOOST_AUTO_TEST_CASE(async_echo)
}
};
op{{}, pt, proc}({}, 0u);
op{pt, proc}({}, 0u);
ctx.run();
}
@@ -252,6 +257,8 @@ BOOST_AUTO_TEST_CASE(async_line)
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";
@@ -276,7 +283,7 @@ BOOST_AUTO_TEST_CASE(async_line)
}
};
op{{}, pt, read}({}, 0u);
op{pt, read}({}, 0u);
ctx.run();
}

View File

@@ -26,6 +26,8 @@ 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)
@@ -46,13 +48,14 @@ struct async_op : net::coroutine
};
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());
//assert(in.echo());
//assert(in.line());
std::vector<std::string> args{argv + 1, argv + argc};
@@ -64,8 +67,6 @@ int main(int argc, char * argv[])
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);
@@ -92,7 +93,7 @@ int main(int argc, char * argv[])
}
else if (async)
{
net::post(ctx, async_op{{}, in, out});
net::post(ctx, async_op{in, out});
ctx.run();
}
else
@@ -110,4 +111,9 @@ int main(int argc, char * argv[])
}
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;
}