1
1
mirror of https://github.com/mawww/kakoune.git synced 2024-12-25 20:41:49 +03:00

move remoting code to remote.cc

ClientAccepter is now Server's implementation detail and all socket logic
are isolated in remote.cc
This commit is contained in:
Maxime Coste 2013-03-13 19:59:39 +01:00
parent b309d1df61
commit 0b45a725e4
3 changed files with 124 additions and 123 deletions

View File

@ -27,14 +27,8 @@
#endif #endif
#include <unordered_map> #include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <locale> #include <locale>
#include <signal.h>
using namespace Kakoune; using namespace Kakoune;
using namespace std::placeholders; using namespace std::placeholders;
@ -686,50 +680,6 @@ std::unordered_map<Key, std::function<void (Context& context)>> keymap =
void run_unit_tests(); void run_unit_tests();
struct Server : public Singleton<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()
{
unlink(m_filename.c_str());
close(m_listener->fd());
}
const String& filename() const { return m_filename; }
private:
String m_filename;
std::unique_ptr<FDWatcher> m_listener;
};
void register_env_vars() void register_env_vars()
{ {
ShellManager& shell_manager = ShellManager::instance(); ShellManager& shell_manager = ShellManager::instance();
@ -804,24 +754,6 @@ void create_local_client(const String& init_command)
std::unique_ptr<UserInterface>{ui}, init_command); std::unique_ptr<UserInterface>{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) void signal_handler(int signal)
{ {
endwin(); endwin();
@ -862,8 +794,9 @@ int main(int argc, char* argv[])
{ {
try try
{ {
std::unique_ptr<RemoteClient> client( auto client = connect_to(parser.option_value("c"),
connect_to(parser.option_value("c"), init_command)); std::unique_ptr<UserInterface>{new NCursesUI{}},
init_command);
while (true) while (true)
event_manager.handle_next_events(); event_manager.handle_next_events();
} }

View File

@ -9,6 +9,11 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
namespace Kakoune namespace Kakoune
{ {
@ -303,9 +308,9 @@ void RemoteUI::set_input_callback(InputCallback callback)
m_input_callback = std::move(callback); m_input_callback = std::move(callback);
} }
RemoteClient::RemoteClient(int socket, UserInterface* ui, RemoteClient::RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui,
const String& init_command) 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(); }} m_socket_watcher{socket, [this](FDWatcher&){ process_next_message(); }}
{ {
Message msg(socket); Message msg(socket);
@ -380,46 +385,112 @@ void RemoteClient::write_next_key()
} }
} }
ClientAccepter::ClientAccepter(int socket) std::unique_ptr<RemoteClient> connect_to(const String& pid, std::unique_ptr<UserInterface>&& ui,
: m_socket_watcher(socket, [this](FDWatcher&) { handle_available_input(); }) {} const String& init_command)
void ClientAccepter::handle_available_input()
{ {
int socket = m_socket_watcher.fd(); auto filename = "/tmp/kak-" + pid;
timeval tv{ 0, 0 };
fd_set rfds; int sock = socket(AF_UNIX, SOCK_STREAM, 0);
do 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<RemoteClient>{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 socket = m_socket_watcher.fd();
int res = ::read(socket, &c, 1); timeval tv{ 0, 0 };
if (res <= 0) fd_set rfds;
do
{ {
if (not m_buffer.empty()) try char c;
int res = ::read(socket, &c, 1);
if (res <= 0)
{ {
Context context{}; if (not m_buffer.empty()) try
CommandManager::instance().execute(m_buffer, context); {
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<UserInterface>{new RemoteUI{socket}}, m_buffer);
delete this;
return;
} }
delete this; else
return; m_buffer += c;
}
if (c == 0) // end of initial command stream, go to interactive ui mode
{
ClientManager::instance().create_client(
std::unique_ptr<UserInterface>{new RemoteUI{socket}}, m_buffer);
delete this;
return;
}
else
m_buffer += c;
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(socket, &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());
} }
} }

View File

@ -10,39 +10,36 @@ namespace Kakoune
struct peer_disconnected {}; 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 // A remote client handle communication between a client running on the server
// and a user interface running on the local process. // and a user interface running on the local process.
class RemoteClient class RemoteClient
{ {
public: public:
RemoteClient(int socket, UserInterface* ui, RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui,
const String& init_command); const String& init_command);
private:
void process_next_message(); void process_next_message();
void write_next_key(); void write_next_key();
private:
std::unique_ptr<UserInterface> m_ui; std::unique_ptr<UserInterface> m_ui;
DisplayCoord m_dimensions; DisplayCoord m_dimensions;
FDWatcher m_socket_watcher; FDWatcher m_socket_watcher;
}; };
std::unique_ptr<RemoteClient> connect_to(const String& pid,
std::unique_ptr<UserInterface>&& ui,
const String& init_command);
struct Server : public Singleton<Server>
{
Server();
~Server();
const String& filename() const { return m_filename; }
private:
String m_filename;
std::unique_ptr<FDWatcher> m_listener;
};
} }