From 856e4853f49b2e95187cd0d030a36a1057454f8e Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 27 May 2020 00:41:01 +0300 Subject: [PATCH] SystemServer: Add BootModes and Environment service options SystemServer will now look at the boot mode, as specified on the kernel command line, and only launch the services configured for that boot mode. --- Base/usr/share/man/man5/SystemServer.md | 4 ++- Services/SystemServer/Service.cpp | 27 +++++++++++++++-- Services/SystemServer/Service.h | 10 +++++-- Services/SystemServer/main.cpp | 40 ++++++++++++------------- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/Base/usr/share/man/man5/SystemServer.md b/Base/usr/share/man/man5/SystemServer.md index 9bc103d891..3e5ceb957d 100644 --- a/Base/usr/share/man/man5/SystemServer.md +++ b/Base/usr/share/man/man5/SystemServer.md @@ -25,7 +25,9 @@ describing how to launch and manage this service. * `Socket` - a path to a socket to create on behalf of the service. For lazy services, SystemServer will actually watch the socket for new connection attempts. An open file descriptor to this socket will be passed as fd 3 to the service. * `SocketPermissions` - (octal) file system permissions for the socket file. The default permissions are 0600. * `User` - a name of the user to run the service as. This impacts what UID, GID (and extra GIDs) the service processes have. By default, services are run as root. -* `WorkingDirectory` - The working directory in which the service is spawned. By Default, services are spawned in the root (`"/"`) directory. +* `WorkingDirectory` - the working directory in which the service is spawned. By default, services are spawned in the root (`"/"`) directory. +* `BootModes` - a comma-separated list of boot modes the service should be enabled in. By default, services are only enabled in the "graphical" mode. The current boot mode is read from the kernel command line, and is assumed to be "graphical" if not specified there. +* `Environment` - a space-separated list of "variable=value" pairs to set in the environment for the service. ## Environment diff --git a/Services/SystemServer/Service.cpp b/Services/SystemServer/Service.cpp index 1f917ba1e6..790ce58c25 100644 --- a/Services/SystemServer/Service.cpp +++ b/Services/SystemServer/Service.cpp @@ -246,6 +246,9 @@ void Service::spawn() } } + for (String& env : m_environment) + putenv(const_cast(env.characters())); + char* argv[m_extra_arguments.size() + 2]; argv[0] = const_cast(m_executable_path.characters()); for (size_t i = 0; i < m_extra_arguments.size(); i++) @@ -321,14 +324,16 @@ Service::Service(const Core::ConfigFile& config, const StringView& name) if (!m_user.is_null()) resolve_user(); + m_working_directory = config.read_entry(name, "WorkingDirectory"); + m_environment = config.read_entry(name, "Environment").split(' '); + m_boot_modes = config.read_entry(name, "BootModes", "graphical").split(','); + m_socket_path = config.read_entry(name, "Socket"); - if (!m_socket_path.is_null()) { + if (!m_socket_path.is_null() && is_enabled()) { auto socket_permissions_string = config.read_entry(name, "SocketPermissions", "0600"); m_socket_permissions = strtol(socket_permissions_string.characters(), nullptr, 8) & 04777; setup_socket(); } - - m_working_directory = config.read_entry(name, "WorkingDirectory"); } void Service::save_to(JsonObject& json) @@ -343,6 +348,16 @@ void Service::save_to(JsonObject& json) for (String& arg : m_extra_arguments) extra_args.append(arg); json.set("extra_arguments", move(extra_args)); + + JsonArray boot_modes; + for (String& mode : m_boot_modes) + boot_modes.append(mode); + json.set("boot_modes", boot_modes); + + JsonArray environment; + for (String& env : m_environment) + boot_modes.append(env); + json.set("environment", environment); */ json.set("stdio_file_path", m_stdio_file_path); @@ -363,3 +378,9 @@ void Service::save_to(JsonObject& json) json.set("restart_attempts", m_restart_attempts); json.set("working_directory", m_working_directory); } + +bool Service::is_enabled() const +{ + extern String g_boot_mode; + return m_boot_modes.contains_slow(g_boot_mode); +} diff --git a/Services/SystemServer/Service.h b/Services/SystemServer/Service.h index de408ec04d..b7cf1679a5 100644 --- a/Services/SystemServer/Service.h +++ b/Services/SystemServer/Service.h @@ -36,6 +36,7 @@ class Service final : public Core::Object { C_OBJECT(Service) public: + bool is_enabled() const; void activate(); void did_exit(int exit_code); @@ -68,6 +69,12 @@ private: uid_t m_uid { 0 }; gid_t m_gid { 0 }; Vector m_extra_gids; + // The working directory in which to spawn the service. + String m_working_directory; + // Boot modes to run this service in. By default, this is the graphical mode. + Vector m_boot_modes; + // Environment variables to pass to the service. + Vector m_environment; // PID of the running instance of this service. pid_t m_pid { -1 }; @@ -81,9 +88,6 @@ private: // times where it has exited unsuccessfully and too quickly. int m_restart_attempts { 0 }; - // The working directory in which to spawn the service - String m_working_directory; - void resolve_user(); void setup_socket(); void setup_notifier(); diff --git a/Services/SystemServer/main.cpp b/Services/SystemServer/main.cpp index fae93e78f9..e19b4891a1 100644 --- a/Services/SystemServer/main.cpp +++ b/Services/SystemServer/main.cpp @@ -38,6 +38,8 @@ #include #include +String g_boot_mode = "graphical"; + static void sigchld_handler(int) { int status = 0; @@ -62,28 +64,22 @@ static void sigchld_handler(int) Core::EventLoop::wake(); } -static void check_for_test_mode() +static void parse_boot_mode() { auto f = Core::File::construct("/proc/cmdline"); if (!f->open(Core::IODevice::ReadOnly)) { dbg() << "Failed to read command line: " << f->error_string(); - ASSERT(false); + return; } - const String cmd = String::copy(f->read_all()); - dbg() << "Read command line: " << cmd; - if (cmd.matches("*testmode=1*")) { - // Eventually, we should run a test binary and wait for it to finish - // before shutting down. But this is good enough for now. - dbg() << "Waiting for testmode shutdown..."; - sleep(5); - dbg() << "Shutting down due to testmode..."; - if (fork() == 0) { - execl("/bin/shutdown", "/bin/shutdown", "-n", nullptr); - ASSERT_NOT_REACHED(); - } - } else { - dbg() << "Continuing normally"; + const String cmdline = String::copy(f->read_all(), Chomp); + dbg() << "Read command line: " << cmdline; + + for (auto& part : cmdline.split_view(' ')) { + auto pair = part.split_view('=', 2); + if (pair.size() == 2 && pair[0] == "boot_mode") + g_boot_mode = pair[1]; } + dbg() << "Booting in " << g_boot_mode << " mode"; } static void mount_all_filesystems() @@ -111,6 +107,7 @@ int main(int, char**) } mount_all_filesystems(); + parse_boot_mode(); struct sigaction sa = { .sa_handler = sigchld_handler, @@ -125,15 +122,16 @@ int main(int, char**) // This takes care of setting up sockets. NonnullRefPtrVector services; auto config = Core::ConfigFile::get_for_system("SystemServer"); - for (auto name : config->groups()) - services.append(Service::construct(*config, name)); + for (auto name : config->groups()) { + auto service = Service::construct(*config, name); + if (service->is_enabled()) + services.append(service); + } // After we've set them all up, activate them! + dbg() << "Activating " << services.size() << " services..."; for (auto& service : services) service.activate(); - // This won't return if we're in test mode. - check_for_test_mode(); - return event_loop.exec(); }