mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-07 04:29:30 -10:00
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:
1 parent
dd293dcef1
commit
f680882469
4 files changed
+49
-41
No files matched your search
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {};
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user