From 0b45a725e4aa5129339f225ed75fbff1a04dbf09 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Wed, 13 Mar 2013 19:59:39 +0100 Subject: [PATCH] move remoting code to remote.cc ClientAccepter is now Server's implementation detail and all socket logic are isolated in remote.cc --- src/main.cc | 75 ++------------------------- src/remote.cc | 137 ++++++++++++++++++++++++++++++++++++++------------ src/remote.hh | 35 ++++++------- 3 files changed, 124 insertions(+), 123 deletions(-) diff --git a/src/main.cc b/src/main.cc index daac24b5..a4f21e33 100644 --- a/src/main.cc +++ b/src/main.cc @@ -27,14 +27,8 @@ #endif #include -#include -#include -#include -#include -#include -#include - #include +#include using namespace Kakoune; using namespace std::placeholders; @@ -686,50 +680,6 @@ std::unordered_map> keymap = void run_unit_tests(); -struct Server : public Singleton -{ - Server() - { - m_filename = "/tmp/kak-" + int_to_str(getpid()); - - int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0); - fcntl(listen_sock, F_SETFD, FD_CLOEXEC); - sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, m_filename.c_str(), sizeof(addr.sun_path) - 1); - - if (bind(listen_sock, (sockaddr*) &addr, sizeof(sockaddr_un)) == -1) - throw runtime_error("unable to bind listen socket " + m_filename); - - if (listen(listen_sock, 4) == -1) - throw runtime_error("unable to listen on socket " + m_filename); - - auto accepter = [](FDWatcher& watcher) { - sockaddr_un client_addr; - socklen_t client_addr_len = sizeof(sockaddr_un); - int sock = accept(watcher.fd(), (sockaddr*) &client_addr, &client_addr_len); - if (sock == -1) - throw runtime_error("accept failed"); - fcntl(sock, F_SETFD, FD_CLOEXEC); - - new ClientAccepter{sock}; - }; - m_listener.reset(new FDWatcher{listen_sock, accepter}); - } - - ~Server() - { - unlink(m_filename.c_str()); - close(m_listener->fd()); - } - - const String& filename() const { return m_filename; } - -private: - String m_filename; - std::unique_ptr m_listener; -}; - void register_env_vars() { ShellManager& shell_manager = ShellManager::instance(); @@ -804,24 +754,6 @@ void create_local_client(const String& init_command) std::unique_ptr{ui}, init_command); } -RemoteClient* connect_to(const String& pid, const String& init_command) -{ - auto filename = "/tmp/kak-" + pid; - - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - fcntl(sock, F_SETFD, FD_CLOEXEC); - sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, filename.c_str(), sizeof(addr.sun_path) - 1); - if (connect(sock, (sockaddr*)&addr, sizeof(addr.sun_path)) == -1) - throw runtime_error("connect to " + filename + " failed"); - - NCursesUI* ui = new NCursesUI{}; - RemoteClient* remote_client = new RemoteClient{sock, ui, init_command}; - - return remote_client; -} - void signal_handler(int signal) { endwin(); @@ -862,8 +794,9 @@ int main(int argc, char* argv[]) { try { - std::unique_ptr client( - connect_to(parser.option_value("c"), init_command)); + auto client = connect_to(parser.option_value("c"), + std::unique_ptr{new NCursesUI{}}, + init_command); while (true) event_manager.handle_next_events(); } diff --git a/src/remote.cc b/src/remote.cc index 0b50b670..d8e7949a 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -9,6 +9,11 @@ #include #include +#include +#include +#include +#include + namespace Kakoune { @@ -303,9 +308,9 @@ void RemoteUI::set_input_callback(InputCallback callback) m_input_callback = std::move(callback); } -RemoteClient::RemoteClient(int socket, UserInterface* ui, +RemoteClient::RemoteClient(int socket, std::unique_ptr&& ui, const String& init_command) - : m_ui(ui), m_dimensions(ui->dimensions()), + : m_ui(std::move(ui)), m_dimensions(m_ui->dimensions()), m_socket_watcher{socket, [this](FDWatcher&){ process_next_message(); }} { Message msg(socket); @@ -380,46 +385,112 @@ void RemoteClient::write_next_key() } } -ClientAccepter::ClientAccepter(int socket) - : m_socket_watcher(socket, [this](FDWatcher&) { handle_available_input(); }) {} - -void ClientAccepter::handle_available_input() +std::unique_ptr connect_to(const String& pid, std::unique_ptr&& ui, + const String& init_command) { - int socket = m_socket_watcher.fd(); - timeval tv{ 0, 0 }; - fd_set rfds; - do + auto filename = "/tmp/kak-" + pid; + + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + fcntl(sock, F_SETFD, FD_CLOEXEC); + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, filename.c_str(), sizeof(addr.sun_path) - 1); + if (connect(sock, (sockaddr*)&addr, sizeof(addr.sun_path)) == -1) + throw runtime_error("connect to " + filename + " failed"); + + return std::unique_ptr{new RemoteClient{sock, std::move(ui), init_command}}; +} + + +// 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) + : m_socket_watcher(socket, [this](FDWatcher&) { handle_available_input(); }) {} + +private: + void handle_available_input() { - char c; - int res = ::read(socket, &c, 1); - if (res <= 0) + int socket = m_socket_watcher.fd(); + timeval tv{ 0, 0 }; + fd_set rfds; + do { - if (not m_buffer.empty()) try + char c; + int res = ::read(socket, &c, 1); + if (res <= 0) { - Context context{}; - CommandManager::instance().execute(m_buffer, context); + 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; } - catch (runtime_error& e) + if (c == 0) // end of initial command stream, go to interactive ui mode { - write_debug("error running command '" + m_buffer + "' : " + e.description()); + ClientManager::instance().create_client( + std::unique_ptr{new RemoteUI{socket}}, m_buffer); + delete this; + return; } - 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; + else + m_buffer += c; - FD_ZERO(&rfds); - FD_SET(socket, &rfds); + FD_ZERO(&rfds); + FD_SET(socket, &rfds); + } + while (select(socket+1, &rfds, NULL, NULL, &tv) == 1); } - while (select(socket+1, &rfds, NULL, NULL, &tv) == 1); + + String m_buffer; + FDWatcher m_socket_watcher; +}; + +Server::Server() +{ + m_filename = "/tmp/kak-" + int_to_str(getpid()); + + int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0); + fcntl(listen_sock, F_SETFD, FD_CLOEXEC); + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, m_filename.c_str(), sizeof(addr.sun_path) - 1); + + if (bind(listen_sock, (sockaddr*) &addr, sizeof(sockaddr_un)) == -1) + throw runtime_error("unable to bind listen socket " + m_filename); + + if (listen(listen_sock, 4) == -1) + throw runtime_error("unable to listen on socket " + m_filename); + + auto accepter = [](FDWatcher& watcher) { + sockaddr_un client_addr; + socklen_t client_addr_len = sizeof(sockaddr_un); + int sock = accept(watcher.fd(), (sockaddr*) &client_addr, &client_addr_len); + if (sock == -1) + throw runtime_error("accept failed"); + fcntl(sock, F_SETFD, FD_CLOEXEC); + + new ClientAccepter{sock}; + }; + m_listener.reset(new FDWatcher{listen_sock, accepter}); +} + +Server::~Server() +{ + unlink(m_filename.c_str()); + close(m_listener->fd()); } } diff --git a/src/remote.hh b/src/remote.hh index b9b6e14c..b9576a6f 100644 --- a/src/remote.hh +++ b/src/remote.hh @@ -10,39 +10,36 @@ namespace Kakoune struct peer_disconnected {}; -// 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: - RemoteClient(int socket, UserInterface* ui, + RemoteClient(int socket, std::unique_ptr&& ui, const String& init_command); +private: void process_next_message(); void write_next_key(); -private: std::unique_ptr m_ui; DisplayCoord m_dimensions; FDWatcher m_socket_watcher; }; +std::unique_ptr connect_to(const String& pid, + std::unique_ptr&& ui, + const String& init_command); + +struct Server : public Singleton +{ + Server(); + ~Server(); + const String& filename() const { return m_filename; } + +private: + String m_filename; + std::unique_ptr m_listener; +}; }