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:
parent
df128821b2
commit
856e4853f4
Notes:
sideshowbarker
2024-07-19 06:04:54 +09:00
Author: https://github.com/bugaevc Commit: https://github.com/SerenityOS/serenity/commit/856e4853f49 Pull-request: https://github.com/SerenityOS/serenity/pull/2409 Reviewed-by: https://github.com/alimpfard
4 changed files with 53 additions and 28 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue