2020-01-18 11:38:21 +03:00
|
|
|
/*
|
2020-01-24 16:45:29 +03:00
|
|
|
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
2020-01-18 11:38:21 +03:00
|
|
|
*
|
2021-04-22 11:24:48 +03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 11:38:21 +03:00
|
|
|
*/
|
|
|
|
|
2019-11-26 19:05:09 +03:00
|
|
|
#include "Service.h"
|
2021-01-17 20:39:00 +03:00
|
|
|
#include <AK/Debug.h>
|
2019-11-26 19:05:09 +03:00
|
|
|
#include <AK/HashMap.h>
|
|
|
|
#include <AK/JsonArray.h>
|
|
|
|
#include <AK/JsonObject.h>
|
2023-01-08 02:19:16 +03:00
|
|
|
#include <AK/String.h>
|
2021-04-15 00:35:49 +03:00
|
|
|
#include <AK/StringBuilder.h>
|
2020-02-06 17:04:03 +03:00
|
|
|
#include <LibCore/ConfigFile.h>
|
2022-04-10 21:51:01 +03:00
|
|
|
#include <LibCore/Directory.h>
|
2022-09-06 09:04:06 +03:00
|
|
|
#include <LibCore/SessionManagement.h>
|
2022-02-06 20:54:24 +03:00
|
|
|
#include <LibCore/SocketAddress.h>
|
|
|
|
#include <LibCore/System.h>
|
2023-03-21 18:35:30 +03:00
|
|
|
#include <LibFileSystem/FileSystem.h>
|
2021-04-11 06:53:37 +03:00
|
|
|
#include <fcntl.h>
|
2019-11-26 19:05:09 +03:00
|
|
|
#include <sched.h>
|
|
|
|
#include <stdio.h>
|
2020-01-04 14:17:13 +03:00
|
|
|
#include <sys/ioctl.h>
|
2019-11-26 19:05:09 +03:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2019-11-26 19:19:59 +03:00
|
|
|
static HashMap<pid_t, Service*> s_service_map;
|
2019-11-26 19:05:09 +03:00
|
|
|
|
2019-11-26 19:19:59 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-06-08 19:29:08 +03:00
|
|
|
ErrorOr<void> Service::setup_socket(SocketDescriptor& socket)
|
2019-11-26 19:27:21 +03:00
|
|
|
{
|
2021-04-15 00:35:49 +03:00
|
|
|
VERIFY(socket.fd == -1);
|
2019-11-26 19:27:21 +03:00
|
|
|
|
2022-08-07 17:48:26 +03:00
|
|
|
// Note: The purpose of this syscall is to remove potential left-over of previous portal.
|
|
|
|
// The return value is discarded as sockets are not always there, and unlinking a non-existent path is considered as a failure.
|
|
|
|
(void)Core::System::unlink(socket.path);
|
|
|
|
|
2022-06-08 19:29:08 +03:00
|
|
|
TRY(Core::Directory::create(LexicalPath(socket.path).parent(), Core::Directory::CreateDirectories::Yes));
|
2019-11-26 19:27:21 +03:00
|
|
|
|
|
|
|
// 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.
|
2022-06-08 19:29:08 +03:00
|
|
|
int const socket_fd = TRY(Core::System::socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
|
2021-04-15 00:35:49 +03:00
|
|
|
socket.fd = socket_fd;
|
2019-11-26 19:27:21 +03:00
|
|
|
|
2020-12-26 15:12:49 +03:00
|
|
|
if (m_account.has_value()) {
|
|
|
|
auto& account = m_account.value();
|
2022-06-08 19:29:08 +03:00
|
|
|
TRY(Core::System::fchown(socket_fd, account.uid(), account.gid()));
|
2020-01-03 22:16:49 +03:00
|
|
|
}
|
|
|
|
|
2022-06-08 19:29:08 +03:00
|
|
|
TRY(Core::System::fchmod(socket_fd, socket.permissions));
|
2020-01-03 22:16:49 +03:00
|
|
|
|
2021-04-15 00:35:49 +03:00
|
|
|
auto socket_address = Core::SocketAddress::local(socket.path);
|
2020-08-23 14:47:52 +03:00
|
|
|
auto un_optional = socket_address.to_sockaddr_un();
|
|
|
|
if (!un_optional.has_value()) {
|
2021-04-15 00:35:49 +03:00
|
|
|
dbgln("Socket name {} is too long. BUG! This should have failed earlier!", socket.path);
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY_NOT_REACHED();
|
2020-08-23 14:47:52 +03:00
|
|
|
}
|
|
|
|
auto un = un_optional.value();
|
2019-11-26 19:27:21 +03:00
|
|
|
|
2022-06-08 19:29:08 +03:00
|
|
|
TRY(Core::System::bind(socket_fd, (sockaddr const*)&un, sizeof(un)));
|
|
|
|
TRY(Core::System::listen(socket_fd, 16));
|
|
|
|
return {};
|
2019-11-26 19:27:21 +03:00
|
|
|
}
|
|
|
|
|
2022-06-08 19:29:08 +03:00
|
|
|
ErrorOr<void> Service::setup_sockets()
|
2021-04-15 00:35:49 +03:00
|
|
|
{
|
|
|
|
for (SocketDescriptor& socket : m_sockets)
|
2022-06-08 19:29:08 +03:00
|
|
|
TRY(setup_socket(socket));
|
|
|
|
return {};
|
2021-04-15 00:35:49 +03:00
|
|
|
}
|
|
|
|
|
2019-11-26 19:41:16 +03:00
|
|
|
void Service::setup_notifier()
|
|
|
|
{
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(m_lazy);
|
2021-04-15 00:35:49 +03:00
|
|
|
VERIFY(m_sockets.size() == 1);
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(!m_socket_notifier);
|
2019-11-26 19:41:16 +03:00
|
|
|
|
2023-04-23 21:59:32 +03:00
|
|
|
m_socket_notifier = Core::Notifier::construct(m_sockets[0].fd, Core::Notifier::Type::Read, this);
|
|
|
|
m_socket_notifier->on_activation = [this] {
|
2023-01-08 02:19:16 +03:00
|
|
|
if (auto result = handle_socket_connection(); result.is_error())
|
|
|
|
dbgln("{}", result.release_error());
|
2020-06-08 23:50:21 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-08 02:19:16 +03:00
|
|
|
ErrorOr<void> Service::handle_socket_connection()
|
2020-06-08 23:50:21 +03:00
|
|
|
{
|
2021-04-15 00:35:49 +03:00
|
|
|
VERIFY(m_sockets.size() == 1);
|
2021-02-07 15:03:24 +03:00
|
|
|
dbgln_if(SERVICE_DEBUG, "Ready to read on behalf of {}", name());
|
2021-01-17 20:39:00 +03:00
|
|
|
|
2021-04-15 00:35:49 +03:00
|
|
|
int socket_fd = m_sockets[0].fd;
|
|
|
|
|
2020-06-08 23:50:21 +03:00
|
|
|
if (m_accept_socket_connections) {
|
2023-01-08 02:19:16 +03:00
|
|
|
auto const accepted_fd = TRY(Core::System::accept(socket_fd, nullptr, nullptr));
|
2022-02-06 20:54:24 +03:00
|
|
|
|
2023-01-08 02:19:16 +03:00
|
|
|
TRY(determine_account(accepted_fd));
|
|
|
|
TRY(spawn(accepted_fd));
|
|
|
|
TRY(Core::System::close(accepted_fd));
|
2020-06-08 23:50:21 +03:00
|
|
|
} else {
|
2019-11-26 19:41:16 +03:00
|
|
|
remove_child(*m_socket_notifier);
|
|
|
|
m_socket_notifier = nullptr;
|
2023-01-08 02:19:16 +03:00
|
|
|
TRY(spawn(socket_fd));
|
2020-06-08 23:50:21 +03:00
|
|
|
}
|
2023-01-08 02:19:16 +03:00
|
|
|
return {};
|
2019-11-26 19:41:16 +03:00
|
|
|
}
|
|
|
|
|
2023-01-08 02:19:16 +03:00
|
|
|
ErrorOr<void> Service::activate()
|
2019-11-26 19:41:16 +03:00
|
|
|
{
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(m_pid < 0);
|
2019-11-26 19:41:16 +03:00
|
|
|
|
|
|
|
if (m_lazy)
|
|
|
|
setup_notifier();
|
|
|
|
else
|
2023-01-08 02:19:16 +03:00
|
|
|
TRY(spawn());
|
|
|
|
return {};
|
2019-11-26 19:41:16 +03:00
|
|
|
}
|
|
|
|
|
2023-05-24 21:55:17 +03:00
|
|
|
ErrorOr<void> Service::change_privileges()
|
|
|
|
{
|
|
|
|
// NOTE: Dropping privileges makes sense when SystemServer is running
|
|
|
|
// for a root session.
|
|
|
|
// This could happen when we need to spawn a Service to serve a client with non-user UID/GID.
|
|
|
|
// However, in case the user explicitly specified a username via the User= option, then we must
|
|
|
|
// try to login as at that user, so we can't ignore the failure when it was requested to change
|
|
|
|
// privileges.
|
|
|
|
if (auto current_uid = getuid(); m_account.has_value() && m_account.value().uid() != current_uid) {
|
|
|
|
if (current_uid != 0 && !m_must_login)
|
|
|
|
return {};
|
|
|
|
auto& account = m_account.value();
|
|
|
|
if (auto error_or_void = account.login(); error_or_void.is_error()) {
|
|
|
|
dbgln("Failed to drop privileges (tried to change to GID={}, UID={}), due to {}\n", account.gid(), account.uid(), error_or_void.error());
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
TRY(Core::System::setenv("HOME"sv, account.home_directory(), true));
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-01-08 02:19:16 +03:00
|
|
|
ErrorOr<void> Service::spawn(int socket_fd)
|
2019-11-26 19:05:09 +03:00
|
|
|
{
|
2023-03-21 18:35:30 +03:00
|
|
|
if (!FileSystem::exists(m_executable_path)) {
|
2021-08-07 13:10:45 +03:00
|
|
|
dbgln("{}: binary \"{}\" does not exist, skipping service.", name(), m_executable_path);
|
2023-01-08 02:19:16 +03:00
|
|
|
return Error::from_errno(ENOENT);
|
2021-08-07 13:10:45 +03:00
|
|
|
}
|
|
|
|
|
2021-02-07 15:03:24 +03:00
|
|
|
dbgln_if(SERVICE_DEBUG, "Spawning {}", name());
|
2019-11-26 19:05:09 +03:00
|
|
|
|
2020-02-07 13:32:14 +03:00
|
|
|
m_run_timer.start();
|
2023-01-08 02:19:16 +03:00
|
|
|
pid_t pid = TRY(Core::System::fork());
|
2019-11-26 19:05:09 +03:00
|
|
|
|
2023-01-08 02:19:16 +03:00
|
|
|
if (pid == 0) {
|
2019-11-26 19:05:09 +03:00
|
|
|
// We are the child.
|
2023-10-10 14:30:58 +03:00
|
|
|
if (m_working_directory.has_value())
|
|
|
|
TRY(Core::System::chdir(*m_working_directory));
|
2020-03-16 23:21:02 +03:00
|
|
|
|
2019-11-26 19:05:09 +03:00
|
|
|
struct sched_param p;
|
|
|
|
p.sched_priority = m_priority;
|
|
|
|
int rc = sched_setparam(0, &p);
|
|
|
|
if (rc < 0) {
|
|
|
|
perror("sched_setparam");
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY_NOT_REACHED();
|
2019-11-26 19:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-10-10 14:30:58 +03:00
|
|
|
if (m_stdio_file_path.has_value()) {
|
2020-01-04 14:17:13 +03:00
|
|
|
close(STDIN_FILENO);
|
2023-10-10 14:30:58 +03:00
|
|
|
auto const fd = TRY(Core::System::open(*m_stdio_file_path, O_RDWR, 0));
|
2023-01-08 02:19:16 +03:00
|
|
|
VERIFY(fd == 0);
|
|
|
|
|
2020-01-04 14:17:13 +03:00
|
|
|
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);
|
2020-01-04 15:15:01 +03:00
|
|
|
|
2023-01-08 02:19:16 +03:00
|
|
|
auto const fd = TRY(Core::System::open("/dev/null"sv, O_RDWR));
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(fd == STDIN_FILENO);
|
2020-01-04 15:15:01 +03:00
|
|
|
dup2(STDIN_FILENO, STDOUT_FILENO);
|
|
|
|
dup2(STDIN_FILENO, STDERR_FILENO);
|
2019-11-26 19:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-01-08 02:19:16 +03:00
|
|
|
StringBuilder socket_takeover_builder;
|
2021-04-15 00:35:49 +03:00
|
|
|
|
2020-06-08 23:50:21 +03:00
|
|
|
if (socket_fd >= 0) {
|
2021-04-15 00:35:49 +03:00
|
|
|
// We were spawned by socket activation. We currently only support
|
|
|
|
// single sockets for socket activation, so make sure that's the case.
|
|
|
|
VERIFY(m_sockets.size() == 1);
|
|
|
|
|
|
|
|
int fd = dup(socket_fd);
|
2023-01-08 02:19:16 +03:00
|
|
|
TRY(socket_takeover_builder.try_appendff("{}:{}", m_sockets[0].path, fd));
|
2021-04-15 00:35:49 +03:00
|
|
|
} else {
|
|
|
|
// We were spawned as a regular process, so dup every socket for this
|
|
|
|
// service and let the service know via SOCKET_TAKEOVER.
|
|
|
|
for (unsigned i = 0; i < m_sockets.size(); i++) {
|
|
|
|
SocketDescriptor& socket = m_sockets.at(i);
|
|
|
|
|
|
|
|
int new_fd = dup(socket.fd);
|
|
|
|
if (i != 0)
|
2023-01-08 02:19:16 +03:00
|
|
|
TRY(socket_takeover_builder.try_append(';'));
|
|
|
|
TRY(socket_takeover_builder.try_appendff("{}:{}", socket.path, new_fd));
|
2021-04-15 00:35:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_sockets.is_empty()) {
|
2019-11-26 19:27:21 +03:00
|
|
|
// The new descriptor is !CLOEXEC here.
|
2023-02-04 18:45:06 +03:00
|
|
|
TRY(Core::System::setenv("SOCKET_TAKEOVER"sv, socket_takeover_builder.string_view(), true));
|
2019-11-26 19:27:21 +03:00
|
|
|
}
|
|
|
|
|
2023-05-24 21:55:17 +03:00
|
|
|
TRY(change_privileges());
|
2019-11-26 19:05:09 +03:00
|
|
|
|
2023-02-04 18:45:06 +03:00
|
|
|
TRY(m_environment.view().for_each_split_view(' ', SplitBehavior::Nothing, [&](auto env) {
|
|
|
|
return Core::System::putenv(env);
|
|
|
|
}));
|
|
|
|
|
|
|
|
Vector<StringView, 10> arguments;
|
|
|
|
TRY(arguments.try_append(m_executable_path));
|
|
|
|
TRY(m_extra_arguments.view().for_each_split_view(' ', SplitBehavior::Nothing, [&](auto arg) {
|
|
|
|
return arguments.try_append(arg);
|
|
|
|
}));
|
2020-05-27 00:41:01 +03:00
|
|
|
|
2023-02-04 18:45:06 +03:00
|
|
|
TRY(Core::System::exec(m_executable_path, arguments, Core::System::SearchInPath::No));
|
2020-06-08 23:47:10 +03:00
|
|
|
} else if (!m_multi_instance) {
|
2019-11-26 19:19:59 +03:00
|
|
|
// We are the parent.
|
2020-06-08 23:47:10 +03:00
|
|
|
m_pid = pid;
|
|
|
|
s_service_map.set(pid, this);
|
2019-11-26 19:05:09 +03:00
|
|
|
}
|
2023-01-08 02:19:16 +03:00
|
|
|
|
|
|
|
return {};
|
2019-11-26 19:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-05-06 07:41:01 +03:00
|
|
|
ErrorOr<void> Service::did_exit(int status)
|
2019-11-26 19:19:59 +03:00
|
|
|
{
|
2023-01-02 08:38:53 +03:00
|
|
|
using namespace AK::TimeLiterals;
|
|
|
|
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(m_pid > 0);
|
|
|
|
VERIFY(!m_multi_instance);
|
2019-11-26 19:19:59 +03:00
|
|
|
|
2023-05-06 07:41:01 +03:00
|
|
|
if (WIFEXITED(status))
|
|
|
|
dbgln("Service {} has exited with exit code {}", name(), WEXITSTATUS(status));
|
|
|
|
if (WIFSIGNALED(status))
|
|
|
|
dbgln("Service {} terminated due to signal {}", name(), WTERMSIG(status));
|
2019-11-26 19:19:59 +03:00
|
|
|
|
|
|
|
s_service_map.remove(m_pid);
|
|
|
|
m_pid = -1;
|
|
|
|
|
2020-02-07 13:32:14 +03:00
|
|
|
if (!m_keep_alive)
|
2023-01-08 02:19:16 +03:00
|
|
|
return {};
|
2020-02-07 13:32:14 +03:00
|
|
|
|
2023-01-02 08:38:53 +03:00
|
|
|
auto run_time = m_run_timer.elapsed_time();
|
2024-02-01 19:03:58 +03:00
|
|
|
bool exited_successfully = WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
2020-02-07 13:32:14 +03:00
|
|
|
|
2023-01-02 08:38:53 +03:00
|
|
|
if (!exited_successfully && run_time < 1_sec) {
|
2020-02-07 13:32:14 +03:00
|
|
|
switch (m_restart_attempts) {
|
|
|
|
case 0:
|
2021-01-09 20:51:44 +03:00
|
|
|
dbgln("Trying again");
|
2020-02-07 13:32:14 +03:00
|
|
|
break;
|
|
|
|
case 1:
|
2021-12-19 23:39:12 +03:00
|
|
|
dbgln("Third time's the charm?");
|
2020-02-07 13:32:14 +03:00
|
|
|
break;
|
|
|
|
default:
|
2021-01-17 20:39:00 +03:00
|
|
|
dbgln("Giving up on {}. Good luck!", name());
|
2023-01-08 02:19:16 +03:00
|
|
|
return {};
|
2020-02-07 13:32:14 +03:00
|
|
|
}
|
|
|
|
m_restart_attempts++;
|
|
|
|
}
|
|
|
|
|
2023-01-08 02:19:16 +03:00
|
|
|
TRY(activate());
|
|
|
|
return {};
|
2019-11-26 19:19:59 +03:00
|
|
|
}
|
|
|
|
|
2022-04-01 20:58:27 +03:00
|
|
|
Service::Service(Core::ConfigFile const& config, StringView name)
|
2023-08-06 19:09:39 +03:00
|
|
|
: Core::EventReceiver(nullptr)
|
2019-11-26 19:05:09 +03:00
|
|
|
{
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(config.has_group(name));
|
2019-11-26 19:05:09 +03:00
|
|
|
|
|
|
|
set_name(name);
|
2023-12-16 17:19:34 +03:00
|
|
|
m_executable_path = config.read_entry(name, "Executable", ByteString::formatted("/bin/{}", this->name()));
|
2023-10-10 14:30:58 +03:00
|
|
|
m_extra_arguments = config.read_entry(name, "Arguments");
|
|
|
|
m_stdio_file_path = config.read_entry_optional(name, "StdIO");
|
2019-11-26 19:05:09 +03:00
|
|
|
|
2023-10-10 14:30:58 +03:00
|
|
|
auto prio = config.read_entry_optional(name, "Priority");
|
2019-12-30 20:46:17 +03:00
|
|
|
if (prio == "low")
|
|
|
|
m_priority = 10;
|
2023-10-10 14:30:58 +03:00
|
|
|
else if (prio == "normal" || !prio.has_value())
|
2019-12-30 20:46:17 +03:00
|
|
|
m_priority = 30;
|
2019-11-26 19:05:09 +03:00
|
|
|
else if (prio == "high")
|
2019-12-30 20:46:17 +03:00
|
|
|
m_priority = 50;
|
2019-11-26 19:05:09 +03:00
|
|
|
else
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY_NOT_REACHED();
|
2019-11-26 19:05:09 +03:00
|
|
|
|
2019-11-26 19:19:59 +03:00
|
|
|
m_keep_alive = config.read_bool_entry(name, "KeepAlive");
|
2019-11-26 19:41:16 +03:00
|
|
|
m_lazy = config.read_bool_entry(name, "Lazy");
|
2019-11-26 19:19:59 +03:00
|
|
|
|
2023-10-10 14:30:58 +03:00
|
|
|
m_user = config.read_entry_optional(name, "User");
|
|
|
|
if (m_user.has_value()) {
|
|
|
|
auto result = Core::Account::from_name(*m_user, Core::Account::Read::PasswdOnly);
|
2023-05-24 21:55:17 +03:00
|
|
|
if (result.is_error()) {
|
2020-12-26 15:12:49 +03:00
|
|
|
warnln("Failed to resolve user {}: {}", m_user, result.error());
|
2023-05-24 21:55:17 +03:00
|
|
|
} else {
|
|
|
|
m_must_login = true;
|
2020-12-26 15:12:49 +03:00
|
|
|
m_account = result.value();
|
2023-05-24 21:55:17 +03:00
|
|
|
}
|
2020-12-26 15:12:49 +03:00
|
|
|
}
|
2020-01-03 22:16:49 +03:00
|
|
|
|
2023-10-10 14:30:58 +03:00
|
|
|
m_working_directory = config.read_entry_optional(name, "WorkingDirectory");
|
2023-01-08 02:19:16 +03:00
|
|
|
m_environment = config.read_entry(name, "Environment");
|
2021-10-23 20:15:01 +03:00
|
|
|
m_system_modes = config.read_entry(name, "SystemModes", "graphical").split(',');
|
2020-06-08 23:47:10 +03:00
|
|
|
m_multi_instance = config.read_bool_entry(name, "MultiInstance");
|
2020-06-08 23:50:21 +03:00
|
|
|
m_accept_socket_connections = config.read_bool_entry(name, "AcceptSocketConnections");
|
2020-05-27 00:41:01 +03:00
|
|
|
|
2023-12-16 17:19:34 +03:00
|
|
|
ByteString socket_entry = config.read_entry(name, "Socket");
|
|
|
|
ByteString socket_permissions_entry = config.read_entry(name, "SocketPermissions", "0600");
|
2020-06-08 23:47:10 +03:00
|
|
|
|
2023-10-10 14:30:58 +03:00
|
|
|
if (!socket_entry.is_empty()) {
|
2023-12-16 17:19:34 +03:00
|
|
|
Vector<ByteString> socket_paths = socket_entry.split(',');
|
|
|
|
Vector<ByteString> socket_perms = socket_permissions_entry.split(',');
|
2021-04-15 00:35:49 +03:00
|
|
|
m_sockets.ensure_capacity(socket_paths.size());
|
|
|
|
|
|
|
|
// Need i here to iterate along with all other vectors.
|
|
|
|
for (unsigned i = 0; i < socket_paths.size(); i++) {
|
2022-09-06 09:04:06 +03:00
|
|
|
auto const path = Core::SessionManagement::parse_path_with_sid(socket_paths.at(i));
|
|
|
|
if (path.is_error()) {
|
|
|
|
// FIXME: better error handling for this case.
|
|
|
|
TODO();
|
|
|
|
}
|
2021-04-15 00:35:49 +03:00
|
|
|
|
|
|
|
// Socket path (plus NUL) must fit into the structs sent to the Kernel.
|
2022-09-06 09:04:06 +03:00
|
|
|
VERIFY(path.value().length() < UNIX_PATH_MAX);
|
2021-04-15 00:35:49 +03:00
|
|
|
|
|
|
|
// This is done so that the last permission repeats for every other
|
|
|
|
// socket. So you can define a single permission, and have it
|
|
|
|
// be applied for every socket.
|
|
|
|
mode_t permissions = strtol(socket_perms.at(min(socket_perms.size() - 1, (long unsigned)i)).characters(), nullptr, 8) & 0777;
|
|
|
|
|
2022-09-06 09:04:06 +03:00
|
|
|
m_sockets.empend(path.value(), -1, permissions);
|
2021-04-15 00:35:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lazy requires Socket, but only one.
|
|
|
|
VERIFY(!m_lazy || m_sockets.size() == 1);
|
|
|
|
// AcceptSocketConnections always requires Socket (single), Lazy, and MultiInstance.
|
|
|
|
VERIFY(!m_accept_socket_connections || (m_sockets.size() == 1 && m_lazy && m_multi_instance));
|
2020-06-08 23:47:10 +03:00
|
|
|
// MultiInstance doesn't work with KeepAlive.
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(!m_multi_instance || !m_keep_alive);
|
2022-06-08 19:29:08 +03:00
|
|
|
}
|
2020-06-08 23:47:10 +03:00
|
|
|
|
2022-06-08 19:29:08 +03:00
|
|
|
ErrorOr<NonnullRefPtr<Service>> Service::try_create(Core::ConfigFile const& config, StringView name)
|
|
|
|
{
|
2023-06-25 13:45:56 +03:00
|
|
|
return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Service(config, name)));
|
2019-11-26 19:05:09 +03:00
|
|
|
}
|
|
|
|
|
2023-06-25 13:45:56 +03:00
|
|
|
bool Service::is_enabled_for_system_mode(StringView mode) const
|
2020-05-27 00:41:01 +03:00
|
|
|
{
|
2023-06-25 13:45:56 +03:00
|
|
|
return m_system_modes.contains_slow(mode);
|
2020-05-27 00:41:01 +03:00
|
|
|
}
|
2022-06-09 18:26:05 +03:00
|
|
|
|
|
|
|
ErrorOr<void> Service::determine_account(int fd)
|
|
|
|
{
|
|
|
|
struct ucred creds = {};
|
|
|
|
socklen_t creds_size = sizeof(creds);
|
|
|
|
TRY(Core::System::getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &creds_size));
|
|
|
|
|
2022-10-22 20:52:53 +03:00
|
|
|
m_account = TRY(Core::Account::from_uid(creds.uid, Core::Account::Read::PasswdOnly));
|
2022-06-09 18:26:05 +03:00
|
|
|
return {};
|
|
|
|
}
|
2022-07-17 16:41:01 +03:00
|
|
|
|
|
|
|
Service::~Service()
|
|
|
|
{
|
|
|
|
for (auto& socket : m_sockets) {
|
|
|
|
if (auto rc = remove(socket.path.characters()); rc != 0)
|
|
|
|
dbgln("{}", Error::from_errno(errno));
|
|
|
|
}
|
|
|
|
}
|