Auto fork server when suspending the local client
That way, other clients can still be serviced by the server.
This commit is contained in:
parent
7776c38755
commit
bd01171861
105
src/main.cc
105
src/main.cc
|
@ -242,6 +242,27 @@ void register_options()
|
||||||
"%val{bufname} %val{cursor_line}:%val{cursor_char_column} "_str);
|
"%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)
|
std::unique_ptr<UserInterface> create_local_ui(bool dummy_ui)
|
||||||
{
|
{
|
||||||
struct DummyUI : UserInterface
|
struct DummyUI : UserInterface
|
||||||
|
@ -269,15 +290,52 @@ std::unique_ptr<UserInterface> create_local_ui(bool dummy_ui)
|
||||||
|
|
||||||
struct LocalUI : NCursesUI
|
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()
|
~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();
|
this->NCursesUI::~NCursesUI();
|
||||||
write_stdout("detached from terminal\n");
|
if (fork_server_to_background())
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
sighandler_t m_old_sighup;
|
||||||
|
sighandler_t m_old_sigtstp;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (not isatty(1))
|
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)
|
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);
|
std::move(ui), get_env_vars(), init_command);
|
||||||
|
|
||||||
if (startup_error)
|
if (startup_error)
|
||||||
client->print_status({
|
local_client->print_status({
|
||||||
"error during startup, see *debug* buffer for details",
|
"error during startup, see *debug* buffer for details",
|
||||||
get_face("Error")
|
get_face("Error")
|
||||||
});
|
});
|
||||||
|
|
||||||
signal(SIGHUP, [](int) {
|
|
||||||
if (client)
|
|
||||||
ClientManager::instance().remove_client(*client, false);
|
|
||||||
client = nullptr;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_handler(int signal)
|
void signal_handler(int signal)
|
||||||
|
@ -475,6 +527,19 @@ int run_server(StringView session, StringView init_command,
|
||||||
client_manager.handle_pending_inputs();
|
client_manager.handle_pending_inputs();
|
||||||
buffer_manager.clear_buffer_trash();
|
buffer_manager.clear_buffer_trash();
|
||||||
string_registry.purge_unused();
|
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&) {}
|
catch (const kill_session&) {}
|
||||||
|
@ -657,11 +722,19 @@ int main(int argc, char* argv[])
|
||||||
files.emplace_back(parser[i]);
|
files.emplace_back(parser[i]);
|
||||||
|
|
||||||
StringView session = parser.get_switch("s").value_or(StringView{});
|
StringView session = parser.get_switch("s").value_or(StringView{});
|
||||||
return run_server(session, init_command,
|
try
|
||||||
(bool)parser.get_switch("n"),
|
{
|
||||||
(bool)parser.get_switch("d"),
|
return run_server(session, init_command,
|
||||||
(bool)parser.get_switch("u"),
|
(bool)parser.get_switch("n"),
|
||||||
files);
|
(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)
|
catch (Kakoune::parameter_error& error)
|
||||||
|
|
|
@ -631,11 +631,14 @@ Server::Server(String session_name)
|
||||||
m_listener.reset(new FDWatcher{listen_sock, accepter});
|
m_listener.reset(new FDWatcher{listen_sock, accepter});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::close_session()
|
void Server::close_session(bool do_unlink)
|
||||||
{
|
{
|
||||||
char socket_file[128];
|
if (do_unlink)
|
||||||
format_to(socket_file, "/tmp/kakoune/{}/{}", getpwuid(geteuid())->pw_name, m_session);
|
{
|
||||||
unlink(socket_file);
|
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->close_fd();
|
||||||
m_listener.reset();
|
m_listener.reset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ struct Server : public Singleton<Server>
|
||||||
~Server();
|
~Server();
|
||||||
const String& session() const { return m_session; }
|
const String& session() const { return m_session; }
|
||||||
|
|
||||||
void close_session();
|
void close_session(bool do_unlink = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Accepter;
|
class Accepter;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user