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.
This commit is contained in:
Sergey Bugaev 2020-05-27 00:41:01 +03:00 committed by Andreas Kling
parent df128821b2
commit 856e4853f4
Notes: sideshowbarker 2024-07-19 06:04:54 +09:00
4 changed files with 53 additions and 28 deletions

View file

@ -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

View file

@ -246,6 +246,9 @@ void Service::spawn()
}
}
for (String& env : m_environment)
putenv(const_cast<char*>(env.characters()));
char* argv[m_extra_arguments.size() + 2];
argv[0] = const_cast<char*>(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);
}

View file

@ -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<gid_t> 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<String> m_boot_modes;
// Environment variables to pass to the service.
Vector<String> 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();

View file

@ -38,6 +38,8 @@
#include <sys/wait.h>
#include <unistd.h>
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<Service> 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();
}