LibSSH+SSHServer: Put child-managing features in a parent of ExecData

This parent will contain the data used for streaming stdin/stdout, the
pid of the child process and a few basic methods required for
subsystems. This refactoring will allow us to easily introduce another
kind of child-managing subsystem, used for interactive shells.
This commit is contained in:
Lucas Chollet committed 2026-11-03 09:31:27 +01:00
1 parent dd293dcef1
commit f680882469
4 files changed
+49 -41

No files matched your search

+22 -19
View File
@@ -8,23 +8,7 @@
namespace SSH {
ErrorOr<ExecData> ExecData::create(Core::Process&& process, int fd_stdin, int fd_stdout, int fd_stderr)
{
ExecData exec_data = {
move(process),
TRY(Core::File::adopt_fd(fd_stdin, Core::File::OpenMode::Write)),
TRY(Core::File::adopt_fd(fd_stdout, Core::File::OpenMode::Read)),
TRY(Core::File::adopt_fd(fd_stderr, Core::File::OpenMode::Read)),
};
TRY(exec_data.stdin_->set_blocking(false));
TRY(exec_data.stdout_->set_blocking(false));
TRY(exec_data.stderr_->set_blocking(false));
return exec_data;
}
Coroutine<ErrorOr<BehaviorControl>> ExecData::handle_channel_data(Session& session)
Coroutine<ErrorOr<BehaviorControl>> ChildManagingSystem::handle_channel_data(Session& session)
{
CO_TRY(co_await stdin_->wait_for_state(Core::Notifier::Type::Write));
auto& stream = session.channel_data;
@@ -36,17 +20,36 @@ Coroutine<ErrorOr<BehaviorControl>> ExecData::handle_channel_data(Session& sessi
co_return BehaviorControl::ContinueExecution;
}
void ExecData::handle_channel_eof(Session const& session)
void ChildManagingSystem::handle_channel_eof(Session const& session) const
{
close_stdin_if_required(session);
}
void ExecData::close_stdin_if_required(Session const& session) const
void ChildManagingSystem::close_stdin_if_required(Session const& session) const
{
if (session.channel_data.is_empty() && session.has_received_eof)
stdin_->close();
}
ErrorOr<ExecData> ExecData::create(Core::Process&& process, int fd_stdin, int fd_stdout, int fd_stderr)
{
ExecData exec_data = {
ChildManagingSystem {
.stdin_ = TRY(Core::File::adopt_fd(fd_stdin, Core::File::OpenMode::Write)),
.stdout_ = TRY(Core::File::adopt_fd(fd_stdout, Core::File::OpenMode::Read)),
.child_pid = process.pid(),
},
move(process),
TRY(Core::File::adopt_fd(fd_stderr, Core::File::OpenMode::Read)),
};
TRY(exec_data.stdin_->set_blocking(false));
TRY(exec_data.stdout_->set_blocking(false));
TRY(exec_data.stderr_->set_blocking(false));
return exec_data;
}
bool ExecData::is_ready_to_be_closed() const
{
return exit_status.has_value() && stdout_->is_eof() && stderr_->is_eof();
+12 -9
View File
@@ -35,26 +35,29 @@ struct PtyInfo {
ByteBuffer terminal_modes {};
};
struct ExecData {
static ErrorOr<ExecData> create(Core::Process&& process, int fd_stdin, int fd_stdout, int fd_stderr);
Core::Process child;
struct ChildManagingSystem {
NonnullOwnPtr<Core::File> stdin_;
NonnullOwnPtr<Core::File> stdout_;
NonnullOwnPtr<Core::File> stderr_;
pid_t child_pid {};
Optional<int> exit_status {};
Coroutine<ErrorOr<BehaviorControl>> handle_channel_data(Session&);
void handle_channel_eof(Session const&);
bool is_ready_to_be_closed() const;
void handle_channel_eof(Session const&) const;
private:
void close_stdin_if_required(Session const&) const;
};
struct ExecData : ChildManagingSystem {
static ErrorOr<ExecData> create(Core::Process&& process, int fd_stdin, int fd_stdout, int fd_stderr);
bool is_ready_to_be_closed() const;
Core::Process child;
NonnullOwnPtr<Core::File> stderr_;
};
struct Session : public RefCounted<Session> {
static ErrorOr<NonnullRefPtr<Session>> create(u32 sender_channel_id, u32 window_size, u32 maximum_packet_size);
+13 -11
View File
@@ -731,19 +731,20 @@ ErrorOr<void> SSHClient::handle_channel_exec(NonnullRefPtr<Session> const& sessi
TRY(send_channel_success_message(session));
// FIXME: Make sure to cancel these coroutines if anything goes wrong.
Core::EventLoop::adopt_coroutine(async_stream_std_data(session, [](ExecData& exec_data) -> Core::File& { return *exec_data.stdout_; }, [this](auto&... args) { return send_channel_data(args...); }));
Core::EventLoop::adopt_coroutine(async_stream_std_data(session, [](ExecData& exec_data) -> Core::File& { return *exec_data.stderr_; }, [this](auto&... args) { return send_channel_extended_data(args...); }));
auto const& exec_data = session->system.get<ExecData>();
Core::EventLoop::adopt_coroutine(async_stream_std_data(session, *exec_data.stdout_, [this](auto&... args) { return send_channel_data(args...); }));
Core::EventLoop::adopt_coroutine(async_stream_std_data(session, *exec_data.stderr_, [this](auto&... args) { return send_channel_extended_data(args...); }));
return {};
}
template<typename F, typename F2>
Coroutine<void> SSHClient::async_stream_std_data(NonnullRefPtr<Session> session, F file_extractor, F2 sender)
template<typename F>
Coroutine<void> SSHClient::async_stream_std_data(NonnullRefPtr<Session> session, Core::File& file, F sender)
{
auto maybe_error = co_await [&]() -> Coroutine<ErrorOr<void>> {
while (true) {
auto output_buffer = CO_TRY(ByteBuffer::create_uninitialized(PAGE_SIZE));
auto output = CO_TRY(co_await file_extractor(session->system.template get<ExecData>()).async_read_some(output_buffer));
auto output = CO_TRY(co_await file.async_read_some(output_buffer));
if (output.is_empty())
break;
@@ -978,9 +979,8 @@ ErrorOr<void> SSHClient::manage_child_death()
return {};
for (auto& session : m_sessions) {
if (session->system.has<ExecData>()) {
auto& exec_data = session->system.get<ExecData>();
if (exec_data.child.pid() == result.pid) {
TRY(session->system.visit([&]<DerivedFrom<ChildManagingSystem> T>(T& system) -> ErrorOr<void> {
if (system.child_pid == result.pid) {
bool exited_with_code_0 = true;
if (WIFEXITED(result.status)) {
exited_with_code_0 &= WEXITSTATUS(result.status) == 0;
@@ -989,11 +989,13 @@ ErrorOr<void> SSHClient::manage_child_death()
}
// FIXME: Send the actual exit status.
exec_data.exit_status = exited_with_code_0 ? 0 : -1;
system.exit_status = exited_with_code_0 ? 0 : -1;
TRY(close_session_if_needed(*session));
}
}
return {}; },
[](auto&) -> ErrorOr<void> {
return {};
}));
}
}
}
+2 -2
View File
@@ -97,8 +97,8 @@ private:
ErrorOr<void> send_channel_close(Session&);
ErrorOr<NonnullRefPtr<Session>> find_session(u32 sender_channel_id);
template<typename F, typename F2>
Coroutine<void> async_stream_std_data(NonnullRefPtr<Session>, F file_extractor, F2 sender);
template<typename F>
Coroutine<void> async_stream_std_data(NonnullRefPtr<Session>, Core::File&, F sender);
Coroutine<void> async_stream_data_to_subsystem(NonnullRefPtr<Session>);
ErrorOr<void> manage_child_death();