diff options
Diffstat (limited to 'Userland/Services/SystemServer')
-rw-r--r-- | Userland/Services/SystemServer/CMakeLists.txt | 7 | ||||
-rw-r--r-- | Userland/Services/SystemServer/Service.cpp | 376 | ||||
-rw-r--r-- | Userland/Services/SystemServer/Service.h | 102 | ||||
-rw-r--r-- | Userland/Services/SystemServer/main.cpp | 242 |
4 files changed, 727 insertions, 0 deletions
diff --git a/Userland/Services/SystemServer/CMakeLists.txt b/Userland/Services/SystemServer/CMakeLists.txt new file mode 100644 index 0000000000..fda6eb1bcc --- /dev/null +++ b/Userland/Services/SystemServer/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES + main.cpp + Service.cpp +) + +serenity_bin(SystemServer) +target_link_libraries(SystemServer LibCore) diff --git a/Userland/Services/SystemServer/Service.cpp b/Userland/Services/SystemServer/Service.cpp new file mode 100644 index 0000000000..99b01b5ab6 --- /dev/null +++ b/Userland/Services/SystemServer/Service.cpp @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Service.h" +#include <AK/HashMap.h> +#include <AK/JsonArray.h> +#include <AK/JsonObject.h> +#include <LibCore/ConfigFile.h> +#include <LibCore/File.h> +#include <LibCore/Socket.h> +#include <grp.h> +#include <libgen.h> +#include <pwd.h> +#include <sched.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> + +static HashMap<pid_t, Service*> s_service_map; + +Service* Service::find_by_pid(pid_t pid) +{ + auto it = s_service_map.find(pid); + if (it == s_service_map.end()) + return nullptr; + return (*it).value; +} + +void Service::setup_socket() +{ + ASSERT(!m_socket_path.is_null()); + ASSERT(m_socket_fd == -1); + + auto ok = Core::File::ensure_parent_directories(m_socket_path); + ASSERT(ok); + + // Note: we use SOCK_CLOEXEC here to make sure we don't leak every socket to + // all the clients. We'll make the one we do need to pass down !CLOEXEC later + // after forking off the process. + m_socket_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (m_socket_fd < 0) { + perror("socket"); + ASSERT_NOT_REACHED(); + } + + if (m_account.has_value()) { + auto& account = m_account.value(); + if (fchown(m_socket_fd, account.uid(), account.gid()) < 0) { + perror("fchown"); + ASSERT_NOT_REACHED(); + } + } + + if (fchmod(m_socket_fd, m_socket_permissions) < 0) { + perror("fchmod"); + ASSERT_NOT_REACHED(); + } + + auto socket_address = Core::SocketAddress::local(m_socket_path); + auto un_optional = socket_address.to_sockaddr_un(); + if (!un_optional.has_value()) { + dbg() << "Socket name " << m_socket_path << " is too long. BUG! This should have failed earlier!"; + ASSERT_NOT_REACHED(); + } + auto un = un_optional.value(); + int rc = bind(m_socket_fd, (const sockaddr*)&un, sizeof(un)); + if (rc < 0) { + perror("bind"); + ASSERT_NOT_REACHED(); + } + + rc = listen(m_socket_fd, 16); + if (rc < 0) { + perror("listen"); + ASSERT_NOT_REACHED(); + } +} + +void Service::setup_notifier() +{ + ASSERT(m_lazy); + ASSERT(m_socket_fd >= 0); + ASSERT(!m_socket_notifier); + + m_socket_notifier = Core::Notifier::construct(m_socket_fd, Core::Notifier::Event::Read, this); + m_socket_notifier->on_ready_to_read = [this] { + handle_socket_connection(); + }; +} + +void Service::handle_socket_connection() +{ +#ifdef SERVICE_DEBUG + dbg() << "Ready to read on behalf of " << name(); +#endif + 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(m_socket_fd); + } +} + +void Service::activate() +{ + ASSERT(m_pid < 0); + + if (m_lazy) + setup_notifier(); + else + spawn(m_socket_fd); +} + +void Service::spawn(int socket_fd) +{ +#ifdef SERVICE_DEBUG + dbg() << "Spawning " << name(); +#endif + + m_run_timer.start(); + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + dbg() << "Failed to spawn " << name() << ". Sucks, dude :("; + } else if (pid == 0) { + // We are the child. + + if (!m_working_directory.is_null()) { + if (chdir(m_working_directory.characters()) < 0) { + perror("chdir"); + ASSERT_NOT_REACHED(); + } + } + + struct sched_param p; + p.sched_priority = m_priority; + int rc = sched_setparam(0, &p); + if (rc < 0) { + perror("sched_setparam"); + ASSERT_NOT_REACHED(); + } + + if (!m_stdio_file_path.is_null()) { + close(STDIN_FILENO); + int fd = open_with_path_length(m_stdio_file_path.characters(), m_stdio_file_path.length(), O_RDWR, 0); + ASSERT(fd <= 0); + if (fd < 0) { + perror("open"); + ASSERT_NOT_REACHED(); + } + dup2(STDIN_FILENO, STDOUT_FILENO); + dup2(STDIN_FILENO, STDERR_FILENO); + + if (isatty(STDIN_FILENO)) { + ioctl(STDIN_FILENO, TIOCSCTTY); + } + } else { + if (isatty(STDIN_FILENO)) { + ioctl(STDIN_FILENO, TIOCNOTTY); + } + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + int fd = open("/dev/null", O_RDWR); + ASSERT(fd == STDIN_FILENO); + dup2(STDIN_FILENO, STDOUT_FILENO); + dup2(STDIN_FILENO, STDERR_FILENO); + } + + if (socket_fd >= 0) { + ASSERT(!m_socket_path.is_null()); + ASSERT(socket_fd > 3); + dup2(socket_fd, 3); + // The new descriptor is !CLOEXEC here. + setenv("SOCKET_TAKEOVER", "1", true); + } + + if (m_account.has_value()) { + auto& account = m_account.value(); + if (setgid(account.gid()) < 0 || setgroups(account.extra_gids().size(), account.extra_gids().data()) < 0 || setuid(account.uid()) < 0) { + dbgln("Failed to drop privileges (GID={}, UID={})\n", account.gid(), account.uid()); + exit(1); + } + setenv("HOME", account.home_directory().characters(), true); + } + + 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++) + argv[i + 1] = const_cast<char*>(m_extra_arguments[i].characters()); + argv[m_extra_arguments.size() + 1] = nullptr; + + rc = execv(argv[0], argv); + perror("exec"); + ASSERT_NOT_REACHED(); + } else if (!m_multi_instance) { + // We are the parent. + m_pid = pid; + s_service_map.set(pid, this); + } +} + +void Service::did_exit(int exit_code) +{ + ASSERT(m_pid > 0); + ASSERT(!m_multi_instance); + + dbg() << "Service " << name() << " has exited with exit code " << exit_code; + + s_service_map.remove(m_pid); + m_pid = -1; + + if (!m_keep_alive) + return; + + int run_time_in_msec = m_run_timer.elapsed(); + bool exited_successfully = exit_code == 0; + + if (!exited_successfully && run_time_in_msec < 1000) { + switch (m_restart_attempts) { + case 0: + dbgln("Trying again"); + break; + case 1: + dbgln("Third time's a charm?"); + break; + default: + dbg() << "Giving up on " << name() << ". Good luck!"; + return; + } + m_restart_attempts++; + } + + activate(); +} + +Service::Service(const Core::ConfigFile& config, const StringView& name) + : Core::Object(nullptr) +{ + ASSERT(config.has_group(name)); + + set_name(name); + m_executable_path = config.read_entry(name, "Executable", String::format("/bin/%s", this->name().characters())); + m_extra_arguments = config.read_entry(name, "Arguments", "").split(' '); + m_stdio_file_path = config.read_entry(name, "StdIO"); + + String prio = config.read_entry(name, "Priority"); + if (prio == "low") + m_priority = 10; + else if (prio == "normal" || prio.is_null()) + m_priority = 30; + else if (prio == "high") + m_priority = 50; + else + ASSERT_NOT_REACHED(); + + m_keep_alive = config.read_bool_entry(name, "KeepAlive"); + m_lazy = config.read_bool_entry(name, "Lazy"); + + m_user = config.read_entry(name, "User"); + if (!m_user.is_null()) { + auto result = Core::Account::from_name(m_user.characters()); + if (result.is_error()) + warnln("Failed to resolve user {}: {}", m_user, result.error()); + else + m_account = result.value(); + } + + 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_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); + // Socket path (plus NUL) must fit into the structs sent to the Kernel. + ASSERT(m_socket_path.length() < UNIX_PATH_MAX); + + 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(); + } +} + +void Service::save_to(JsonObject& json) +{ + Core::Object::save_to(json); + + json.set("executable_path", m_executable_path); + + // FIXME: This crashes Inspector. + /* + JsonArray extra_args; + 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); + json.set("priority", m_priority); + json.set("keep_alive", m_keep_alive); + json.set("socket_path", m_socket_path); + json.set("socket_permissions", m_socket_permissions); + json.set("lazy", m_lazy); + json.set("user", m_user); + 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); + else + json.set("pid", nullptr); + + 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/Userland/Services/SystemServer/Service.h b/Userland/Services/SystemServer/Service.h new file mode 100644 index 0000000000..23014825cf --- /dev/null +++ b/Userland/Services/SystemServer/Service.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/RefPtr.h> +#include <AK/String.h> +#include <LibCore/Account.h> +#include <LibCore/ElapsedTimer.h> +#include <LibCore/Notifier.h> +#include <LibCore/Object.h> + +class Service final : public Core::Object { + C_OBJECT(Service) + +public: + bool is_enabled() const; + void activate(); + void did_exit(int exit_code); + + static Service* find_by_pid(pid_t); + + // FIXME: Port to Core::Property + void save_to(AK::JsonObject&); + +private: + Service(const Core::ConfigFile&, const StringView& name); + + void spawn(int socket_fd = -1); + + // Path to the executable. By default this is /bin/{m_name}. + String m_executable_path; + // Extra arguments, starting from argv[1], to pass when exec'ing. + Vector<String> m_extra_arguments; + // File path to open as stdio fds. + String m_stdio_file_path; + int m_priority { 1 }; + // Whether we should re-launch it if it exits. + bool m_keep_alive { false }; + // Path to the socket to create and listen on on behalf of this service. + 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. + String m_user; + // 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; + // Whether several instances of this service can run at once. + bool m_multi_instance { false }; + // Environment variables to pass to the service. + Vector<String> m_environment; + + // The resolved user account to run this service as. + Optional<Core::Account> m_account; + + // For single-instance services, PID of the running instance of this service. + pid_t m_pid { -1 }; + // An open fd to the socket. + int m_socket_fd { -1 }; + RefPtr<Core::Notifier> m_socket_notifier; + + // Timer since we last spawned the service. + Core::ElapsedTimer m_run_timer; + // How many times we have tried to restart this service, only counting those + // times where it has exited unsuccessfully and too quickly. + int m_restart_attempts { 0 }; + + void setup_socket(); + void setup_notifier(); + void handle_socket_connection(); +}; diff --git a/Userland/Services/SystemServer/main.cpp b/Userland/Services/SystemServer/main.cpp new file mode 100644 index 0000000000..6409b0eda6 --- /dev/null +++ b/Userland/Services/SystemServer/main.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Service.h" +#include <AK/Assertions.h> +#include <AK/ByteBuffer.h> +#include <LibCore/ConfigFile.h> +#include <LibCore/Event.h> +#include <LibCore/EventLoop.h> +#include <LibCore/File.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +String g_boot_mode = "graphical"; + +static void sigchld_handler(int) +{ + for (;;) { + int status = 0; + pid_t pid = waitpid(-1, &status, WNOHANG); + if (pid < 0) { + perror("waitpid"); + break; + } + if (pid == 0) + break; + +#ifdef SYSTEMSERVER_DEBUG + dbg() << "Reaped child with pid " << pid << ", exit status " << status; +#endif + + Service* service = Service::find_by_pid(pid); + if (service == nullptr) { + // This can happen for multi-instance services. + continue; + } + + service->did_exit(status); + } +} + +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(); + return; + } + 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 prepare_devfs() +{ + // FIXME: Find a better way to all of this stuff, without hardcoding all of this! + + int rc = mount(-1, "/dev", "dev", 0); + if (rc != 0) { + ASSERT_NOT_REACHED(); + } + + rc = mkdir("/dev/pts", 0755); + if (rc != 0) { + ASSERT_NOT_REACHED(); + } + + rc = mount(-1, "/dev/pts", "devpts", 0); + if (rc != 0) { + ASSERT_NOT_REACHED(); + } + + rc = symlink("/dev/random", "/dev/urandom"); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } + + // FIXME: Find a better way to chown without hardcoding the gid! + // This will fail with ENOENT in text mode. + rc = chown("/dev/fb0", 0, 3); + if (rc < 0 && errno != ENOENT) { + ASSERT_NOT_REACHED(); + } + + // FIXME: Find a better way to chown without hardcoding the gid! + rc = chown("/dev/keyboard", 0, 3); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } + + // FIXME: Find a better way to chown without hardcoding the gid! + rc = chown("/dev/mouse", 0, 3); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } + + for (size_t index = 0; index < 4; index++) { + // FIXME: Find a better way to chown without hardcoding the gid! + rc = chown(String::formatted("/dev/tty{}", index).characters(), 0, 2); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } + } + + for (size_t index = 0; index < 4; index++) { + // FIXME: Find a better way to chown without hardcoding the gid! + rc = chown(String::formatted("/dev/ttyS{}", index).characters(), 0, 2); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } + } + + // FIXME: Find a better way to chown without hardcoding the gid! + rc = chown("/dev/audio", 0, 4); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } + + rc = symlink("/proc/self/fd/0", "/dev/stdin"); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } + rc = symlink("/proc/self/fd/1", "/dev/stdout"); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } + rc = symlink("/proc/self/fd/2", "/dev/stderr"); + if (rc < 0) { + ASSERT_NOT_REACHED(); + } +} + +static void mount_all_filesystems() +{ + dbgln("Spawning mount -a to mount all filesystems."); + pid_t pid = fork(); + + if (pid < 0) { + perror("fork"); + ASSERT_NOT_REACHED(); + } else if (pid == 0) { + execl("/bin/mount", "mount", "-a", nullptr); + perror("exec"); + ASSERT_NOT_REACHED(); + } else { + wait(nullptr); + } +} + +static void create_tmp_rpc_directory() +{ + dbgln("Creating /tmp/rpc directory"); + auto old_umask = umask(0); + auto rc = mkdir("/tmp/rpc", 01777); + if (rc < 0) { + perror("mkdir(/tmp/rpc)"); + ASSERT_NOT_REACHED(); + } + umask(old_umask); +} + +static void create_tmp_coredump_directory() +{ + dbgln("Creating /tmp/coredump directory"); + auto old_umask = umask(0); + auto rc = mkdir("/tmp/coredump", 0755); + if (rc < 0) { + perror("mkdir(/tmp/coredump)"); + ASSERT_NOT_REACHED(); + } + umask(old_umask); +} + +int main(int, char**) +{ + prepare_devfs(); + + if (pledge("stdio proc exec tty accept unix rpath wpath cpath chown fattr id sigaction", nullptr) < 0) { + perror("pledge"); + return 1; + } + + mount_all_filesystems(); + create_tmp_rpc_directory(); + create_tmp_coredump_directory(); + parse_boot_mode(); + + Core::EventLoop event_loop; + + event_loop.register_signal(SIGCHLD, sigchld_handler); + + // Read our config and instantiate services. + // This takes care of setting up sockets. + NonnullRefPtrVector<Service> services; + auto config = Core::ConfigFile::get_for_system("SystemServer"); + 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(); + + return event_loop.exec(); +} |