From e428a9757f4dd51a9ec0b9e913ca61424f060a4c Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 12 Mar 2013 18:53:18 +0100 Subject: [PATCH] Modify network protocol to allow commands through the socket --- src/main.cc | 2 +- src/remote.cc | 49 +++++++++++++++++++++++++++++++++++++++++-------- src/remote.hh | 19 ++++++++++++++++++- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/main.cc b/src/main.cc index 04407598..0f661b1d 100644 --- a/src/main.cc +++ b/src/main.cc @@ -712,7 +712,7 @@ struct Server : public Singleton throw runtime_error("accept failed"); fcntl(sock, F_SETFD, FD_CLOEXEC); - new FDWatcher{sock, handle_remote}; + new ClientAccepter{sock}; }; m_listener.reset(new FDWatcher{listen_sock, accepter}); } diff --git a/src/remote.cc b/src/remote.cc index f46a0487..0b50b670 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -4,6 +4,8 @@ #include "debug.hh" #include "client_manager.hh" #include "event_manager.hh" +#include "buffer_manager.hh" +#include "command_manager.hh" #include #include @@ -307,7 +309,7 @@ RemoteClient::RemoteClient(int socket, UserInterface* ui, m_socket_watcher{socket, [this](FDWatcher&){ process_next_message(); }} { Message msg(socket); - msg.write(init_command); + msg.write(init_command.c_str(), (int)init_command.length()+1); Key key{ resize_modifier, Codepoint(((int)m_dimensions.line << 16) | (int)m_dimensions.column) }; msg.write(key); @@ -378,15 +380,46 @@ void RemoteClient::write_next_key() } } -void handle_remote(FDWatcher& watcher) +ClientAccepter::ClientAccepter(int socket) + : m_socket_watcher(socket, [this](FDWatcher&) { handle_available_input(); }) {} + +void ClientAccepter::handle_available_input() { - int socket = watcher.fd(); - delete &watcher; - String init_command = read(socket); + int socket = m_socket_watcher.fd(); + timeval tv{ 0, 0 }; + fd_set rfds; + do + { + char c; + int res = ::read(socket, &c, 1); + if (res <= 0) + { + if (not m_buffer.empty()) try + { + Context context{}; + CommandManager::instance().execute(m_buffer, context); + } + catch (runtime_error& e) + { + write_debug("error running command '" + m_buffer + "' : " + e.description()); + } + delete this; + return; + } + if (c == 0) // end of initial command stream, go to interactive ui mode + { + ClientManager::instance().create_client( + std::unique_ptr{new RemoteUI{socket}}, m_buffer); + delete this; + return; + } + else + m_buffer += c; - RemoteUI* ui = new RemoteUI{socket}; - ClientManager::instance().create_client( - std::unique_ptr{ui}, init_command); + FD_ZERO(&rfds); + FD_SET(socket, &rfds); + } + while (select(socket+1, &rfds, NULL, NULL, &tv) == 1); } } diff --git a/src/remote.hh b/src/remote.hh index b7a26681..b9b6e14c 100644 --- a/src/remote.hh +++ b/src/remote.hh @@ -10,8 +10,25 @@ namespace Kakoune struct peer_disconnected {}; -void handle_remote(FDWatcher& event); +// A client accepter handle a connection until it closes or a nul byte is +// recieved. Everything recieved before is considered to be a command. +// +// * When a nul byte is recieved, the socket is handed to a new Client along +// with the command. +// * When the connection is closed, the command is run in an empty context. +class ClientAccepter +{ +public: + ClientAccepter(int socket); +private: + void handle_available_input(); + String m_buffer; + FDWatcher m_socket_watcher; +}; + +// A remote client handle communication between a client running on the server +// and a user interface running on the local process. class RemoteClient { public: