#pragma once #include #include #include #include #include "event_bus.h" #include "settings.h" #include "ssh_hosts.h" #include "terminal.h" namespace roro { // One SSH session to a shell (issue #2, docs/milestones/N1.md): the protocol is libssh's, on a // task of its own; what it prints goes into a term::Terminal, which the SSH App draws. The // session lasts until the far end closes it or disconnect() is called, whether the App is in // front or not. // // The task and the main loop share the terminal, the bytes waiting to be sent and the state, // under one lock. Questions for the user (is this the right server? what is the password?) are // states the task waits in until the App answers. class SshService { public: enum class State { Idle, Connecting, AskTrust, AskPassword, Open, Ended }; static constexpr size_t kNeedFree = 75 * 1024; // a session peaks at about 63 KB (measured) static constexpr int kHistory = 100; explicit SshService(Settings& settings); // "" when the connection is on its way, or why not. std::string connect(const term::SshTarget& target, int cols, int rows); void disconnect(); // from any state; the terminal stays to be read until the next connect void clear(); // Ended, and read: the terminal and its history are given back State state() const { return state_; } bool active() const { return state_ == State::Open; } // for the Status Bar bool opened() const { return opened_; } // this session got as far as a shell std::string status() const; // what it is doing, or why it ended const term::SshTarget& target() const { return target_; } // AskTrust: the server's fingerprint, and the one remembered if this is a different one. std::string fingerprint() const; std::string remembered() const; void answerTrust(bool yes); // AskPassword. void answerPassword(const std::string& password); // Open (and Ended, to read what is left). bool hasTerminal() const { return static_cast(term_); } void withTerminal(const std::function& use); uint32_t revision() const { return revision_; } void send(const std::string& bytes); void resize(int cols, int rows); // This device's own key: made once, its public half is what goes on servers. bool hasKey() const { return !settings_.getString(Setting::SshKey).empty(); } std::string publicKey() const { return settings_.getString(Setting::SshPublic); } std::string makeKey(); // "" or why not private: struct Run; // what one connection's task works with static void task(void* arg); void session(Run& run); void end(const std::string& why); void say(const std::string& what); Settings& settings_; void* lock_; // SemaphoreHandle_t std::atomic state_{State::Idle}; std::atomic stop_{false}, answered_{false}, trusted_{false}, running_{false}, opened_{false}; std::atomic revision_{0}; term::SshTarget target_; std::unique_ptr term_; std::string status_, fingerprint_, remembered_, password_, outgoing_; int wantCols_ = 0, wantRows_ = 0; bool resized_ = false; }; } // namespace roro