summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2015-10-08 20:05:47 +0100
committerMaxime Coste <frrrwww@gmail.com>2015-10-08 20:05:47 +0100
commitbd0117186128f8810fe602ee7fba2f71cefe89c1 (patch)
treef364010b0c4aaeb3ce928e242cb1941ae75a304f
parent7776c38755bfa4674b21989d7a7ef9561c02c366 (diff)
Auto fork server when suspending the local client
That way, other clients can still be serviced by the server.
-rw-r--r--src/main.cc105
-rw-r--r--src/remote.cc11
-rw-r--r--src/remote.hh2
3 files changed, 97 insertions, 21 deletions
diff --git a/src/main.cc b/src/main.cc
index 31bc2cfa..9da4ddb3 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -242,6 +242,27 @@ void register_options()
"%val{bufname} %val{cursor_line}:%val{cursor_char_column} "_str);
}
+struct convert_to_client_mode
+{
+ String session;
+};
+
+static Client* local_client = nullptr;
+static bool convert_to_client_pending = false;
+
+pid_t fork_server_to_background()
+{
+ if (pid_t pid = fork())
+ return pid;
+
+ if (fork()) // double fork to orphan the server
+ exit(0);
+
+ write_stderr(format("Kakoune forked server to background ({}), for session '{}'\n",
+ getpid(), Server::instance().session()));
+ return 0;
+}
+
std::unique_ptr<UserInterface> create_local_ui(bool dummy_ui)
{
struct DummyUI : UserInterface
@@ -269,15 +290,52 @@ std::unique_ptr<UserInterface> create_local_ui(bool dummy_ui)
struct LocalUI : NCursesUI
{
+ LocalUI()
+ {
+ m_old_sighup = signal(SIGHUP, [](int) {
+ ClientManager::instance().remove_client(*local_client, false);
+ });
+
+ m_old_sigtstp = signal(SIGTSTP, [](int) {
+ if (ClientManager::instance().count() == 1 and
+ *ClientManager::instance().begin() == local_client)
+ {
+ // Suspend normally if we are the only client
+ auto current = signal(SIGTSTP, static_cast<LocalUI&>(local_client->ui()).m_old_sigtstp);
+
+ sigset_t unblock_sigtstp, old_mask;
+ sigemptyset(&unblock_sigtstp);
+ sigaddset(&unblock_sigtstp, SIGTSTP);
+ sigprocmask(SIG_UNBLOCK, &unblock_sigtstp, &old_mask);
+
+ raise(SIGTSTP);
+
+ sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+
+ signal(SIGTSTP, current);
+ }
+ else
+ convert_to_client_pending = true;
+ });
+ }
+
~LocalUI()
{
- if (not ClientManager::instance().empty() and fork())
+ signal(SIGHUP, m_old_sighup);
+ signal(SIGTSTP, m_old_sigtstp);
+ local_client = nullptr;
+ if (not convert_to_client_pending and
+ not ClientManager::instance().empty())
{
this->NCursesUI::~NCursesUI();
- write_stdout("detached from terminal\n");
- exit(0);
+ if (fork_server_to_background())
+ exit(0);
}
}
+
+ private:
+ sighandler_t m_old_sighup;
+ sighandler_t m_old_sigtstp;
};
if (not isatty(1))
@@ -298,20 +356,14 @@ std::unique_ptr<UserInterface> create_local_ui(bool dummy_ui)
void create_local_client(std::unique_ptr<UserInterface> ui, StringView init_command, bool startup_error)
{
- static Client* client = ClientManager::instance().create_client(
+ local_client = ClientManager::instance().create_client(
std::move(ui), get_env_vars(), init_command);
if (startup_error)
- client->print_status({
+ local_client->print_status({
"error during startup, see *debug* buffer for details",
get_face("Error")
});
-
- signal(SIGHUP, [](int) {
- if (client)
- ClientManager::instance().remove_client(*client, false);
- client = nullptr;
- });
}
void signal_handler(int signal)
@@ -475,6 +527,19 @@ int run_server(StringView session, StringView init_command,
client_manager.handle_pending_inputs();
buffer_manager.clear_buffer_trash();
string_registry.purge_unused();
+
+ if (convert_to_client_pending)
+ {
+ ClientManager::instance().remove_client(*local_client, true);
+ convert_to_client_pending = false;
+
+ if (fork_server_to_background())
+ {
+ String session = server.session();
+ server.close_session(false);
+ throw convert_to_client_mode{ std::move(session) };
+ }
+ }
}
}
catch (const kill_session&) {}
@@ -657,11 +722,19 @@ int main(int argc, char* argv[])
files.emplace_back(parser[i]);
StringView session = parser.get_switch("s").value_or(StringView{});
- return run_server(session, init_command,
- (bool)parser.get_switch("n"),
- (bool)parser.get_switch("d"),
- (bool)parser.get_switch("u"),
- files);
+ try
+ {
+ return run_server(session, init_command,
+ (bool)parser.get_switch("n"),
+ (bool)parser.get_switch("d"),
+ (bool)parser.get_switch("u"),
+ files);
+ }
+ catch (convert_to_client_mode& convert)
+ {
+ raise(SIGTSTP);
+ return run_client(convert.session, "echo converted to client only mode");
+ }
}
}
catch (Kakoune::parameter_error& error)
diff --git a/src/remote.cc b/src/remote.cc
index 1e1edc23..b4201abc 100644
--- a/src/remote.cc
+++ b/src/remote.cc
@@ -631,11 +631,14 @@ Server::Server(String session_name)
m_listener.reset(new FDWatcher{listen_sock, accepter});
}
-void Server::close_session()
+void Server::close_session(bool do_unlink)
{
- char socket_file[128];
- format_to(socket_file, "/tmp/kakoune/{}/{}", getpwuid(geteuid())->pw_name, m_session);
- unlink(socket_file);
+ if (do_unlink)
+ {
+ char socket_file[128];
+ format_to(socket_file, "/tmp/kakoune/{}/{}", getpwuid(geteuid())->pw_name, m_session);
+ unlink(socket_file);
+ }
m_listener->close_fd();
m_listener.reset();
}
diff --git a/src/remote.hh b/src/remote.hh
index 3671521c..1164d2be 100644
--- a/src/remote.hh
+++ b/src/remote.hh
@@ -48,7 +48,7 @@ struct Server : public Singleton<Server>
~Server();
const String& session() const { return m_session; }
- void close_session();
+ void close_session(bool do_unlink = true);
private:
class Accepter;