diff --git a/Services/SystemServer/Service.cpp b/Services/SystemServer/Service.cpp index d4fe6a98fd..cb106e1e91 100644 --- a/Services/SystemServer/Service.cpp +++ b/Services/SystemServer/Service.cpp @@ -158,11 +158,26 @@ void Service::setup_notifier() m_socket_notifier = Core::Notifier::construct(m_socket_fd, Core::Notifier::Event::Read, this); m_socket_notifier->on_ready_to_read = [this] { - dbg() << "Ready to read on behalf of " << name(); + handle_socket_connection(); + }; +} + +void Service::handle_socket_connection() +{ + dbg() << "Ready to read on behalf of " << name(); + if (m_accept_socket_connections) { + int accepted_fd = accept(m_socket_fd, nullptr, nullptr); + if (accepted_fd < 0) { + perror("accept"); + return; + } + spawn(accepted_fd); + close(accepted_fd); + } else { remove_child(*m_socket_notifier); m_socket_notifier = nullptr; - spawn(); - }; + spawn(m_socket_fd); + } } void Service::activate() @@ -172,10 +187,10 @@ void Service::activate() if (m_lazy) setup_notifier(); else - spawn(); + spawn(m_socket_fd); } -void Service::spawn() +void Service::spawn(int socket_fd) { dbg() << "Spawning " << name(); @@ -231,11 +246,12 @@ void Service::spawn() dup2(STDIN_FILENO, STDERR_FILENO); } - if (!m_socket_path.is_null()) { - ASSERT(m_socket_fd > 2); - dup2(m_socket_fd, 3); + if (socket_fd >= 0) { + ASSERT(!m_socket_path.is_null()); + ASSERT(socket_fd > 2); + dup2(socket_fd, 3); // The new descriptor is !CLOEXEC here. - // This is true even if m_socket_fd == 3. + // This is true even if socket_fd == 3. setenv("SOCKET_TAKEOVER", "1", true); } @@ -330,11 +346,14 @@ Service::Service(const Core::ConfigFile& config, const StringView& name) m_environment = config.read_entry(name, "Environment").split(' '); m_boot_modes = config.read_entry(name, "BootModes", "graphical").split(','); m_multi_instance = config.read_bool_entry(name, "MultiInstance"); + m_accept_socket_connections = config.read_bool_entry(name, "AcceptSocketConnections"); m_socket_path = config.read_entry(name, "Socket"); // Lazy requires Socket. ASSERT(!m_lazy || !m_socket_path.is_null()); + // AcceptSocketConnections always requires Socket, Lazy, and MultiInstance. + ASSERT(!m_accept_socket_connections || (!m_socket_path.is_null() && m_lazy && m_multi_instance)); // MultiInstance doesn't work with KeepAlive. ASSERT(!m_multi_instance || !m_keep_alive); @@ -379,6 +398,7 @@ void Service::save_to(JsonObject& json) json.set("uid", m_uid); json.set("gid", m_gid); json.set("multi_instance", m_multi_instance); + json.set("accept_socket_connections", m_accept_socket_connections); if (m_pid > 0) json.set("pid", m_pid); diff --git a/Services/SystemServer/Service.h b/Services/SystemServer/Service.h index bfe143351c..1d293044fb 100644 --- a/Services/SystemServer/Service.h +++ b/Services/SystemServer/Service.h @@ -47,7 +47,7 @@ public: private: Service(const Core::ConfigFile&, const StringView& name); - void spawn(); + void spawn(int socket_fd = -1); // Path to the executable. By default this is /bin/{m_name}. String m_executable_path; @@ -62,6 +62,10 @@ private: String m_socket_path; // File system permissions for the socket. mode_t m_socket_permissions { 0 }; + // Whether we should accept connections on the socket and pass the accepted + // (and not listening) socket to the service. This requires a multi-instance + // service. + bool m_accept_socket_connections { false }; // Whether we should only spawn this service once somebody connects to the socket. bool m_lazy; // The name of the user we should run this service as. @@ -93,4 +97,5 @@ private: void resolve_user(); void setup_socket(); void setup_notifier(); + void handle_socket_connection(); };