mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 02:12:09 -05:00
5b95850e28
SystemServer only allowed a single socket to be created for a service before this. Now, SystemServer will allow any amount of sockets. The sockets can be defined like so: [SomeService] Socket=/tmp/portal/socket1,/tmp/portal/socket2,/tmp/portal/socket3 SocketPermissions=660,600 The last item in SocketPermissions is applied to the remainder of the sockets in the Socket= line, so multiple sockets can have the same permissions without having to repeat them. Defining multiple sockets is not allowed for socket-activated services at the moment, and wouldn't make much sense anyway. This patch also makes socket takeovers more robust by removing the assumption that the socket will always be passed in fd 3. Now, the SOCKET_TAKEOVER environment variable carries information about which endpoint corresponds to which socket, like so: SOCKET_TAKEOVER=/tmp/portal/socket1:3 /tmp/portal/socket2:4 and LocalServer/LocalService will parse this automatically and select the correct one. The old behavior of getting the default socket is preserved so long as the service only requests a single socket in SystemServer.ini.
173 lines
4.8 KiB
C++
173 lines
4.8 KiB
C++
/*
|
|
* Copyright (c) 2018-2020, 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 <LibCore/LocalServer.h>
|
|
#include <LibCore/LocalSocket.h>
|
|
#include <LibCore/Notifier.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#ifndef SOCK_NONBLOCK
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
|
|
namespace Core {
|
|
|
|
LocalServer::LocalServer(Object* parent)
|
|
: Object(parent)
|
|
{
|
|
}
|
|
|
|
LocalServer::~LocalServer()
|
|
{
|
|
if (m_fd >= 0)
|
|
::close(m_fd);
|
|
}
|
|
|
|
bool LocalServer::take_over_from_system_server(String const& socket_path)
|
|
{
|
|
if (m_listening)
|
|
return false;
|
|
|
|
if (!LocalSocket::s_overtaken_sockets_parsed)
|
|
LocalSocket::parse_sockets_from_system_server();
|
|
|
|
int fd = -1;
|
|
if (socket_path.is_null()) {
|
|
// We want the first (and only) socket.
|
|
if (LocalSocket::s_overtaken_sockets.size() == 1) {
|
|
fd = LocalSocket::s_overtaken_sockets.begin()->value;
|
|
}
|
|
} else {
|
|
auto it = LocalSocket::s_overtaken_sockets.find(socket_path);
|
|
if (it != LocalSocket::s_overtaken_sockets.end()) {
|
|
fd = it->value;
|
|
}
|
|
}
|
|
|
|
if (fd >= 0) {
|
|
// Sanity check: it has to be a socket.
|
|
struct stat stat;
|
|
int rc = fstat(fd, &stat);
|
|
if (rc == 0 && S_ISSOCK(stat.st_mode)) {
|
|
// The SystemServer has passed us the socket, so use that instead of
|
|
// creating our own.
|
|
m_fd = fd;
|
|
// It had to be !CLOEXEC for obvious reasons, but we
|
|
// don't need it to be !CLOEXEC anymore, so set the
|
|
// CLOEXEC flag now.
|
|
fcntl(m_fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
m_listening = true;
|
|
setup_notifier();
|
|
return true;
|
|
} else {
|
|
if (rc != 0)
|
|
perror("fstat");
|
|
dbgln("It's not a socket, what the heck??");
|
|
}
|
|
}
|
|
|
|
dbgln("Failed to take the socket over from SystemServer");
|
|
|
|
return false;
|
|
}
|
|
|
|
void LocalServer::setup_notifier()
|
|
{
|
|
m_notifier = Notifier::construct(m_fd, Notifier::Event::Read, this);
|
|
m_notifier->on_ready_to_read = [this] {
|
|
if (on_ready_to_accept)
|
|
on_ready_to_accept();
|
|
};
|
|
}
|
|
|
|
bool LocalServer::listen(const String& address)
|
|
{
|
|
if (m_listening)
|
|
return false;
|
|
|
|
int rc;
|
|
|
|
#ifdef SOCK_NONBLOCK
|
|
m_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
|
#else
|
|
m_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
|
int option = 1;
|
|
ioctl(m_fd, FIONBIO, &option);
|
|
fcntl(m_fd, F_SETFD, FD_CLOEXEC);
|
|
#endif
|
|
VERIFY(m_fd >= 0);
|
|
#ifndef __APPLE__
|
|
rc = fchmod(m_fd, 0600);
|
|
if (rc < 0) {
|
|
perror("fchmod");
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
#endif
|
|
|
|
auto socket_address = SocketAddress::local(address);
|
|
auto un_optional = socket_address.to_sockaddr_un();
|
|
if (!un_optional.has_value()) {
|
|
perror("bind");
|
|
return false;
|
|
}
|
|
auto un = un_optional.value();
|
|
rc = ::bind(m_fd, (const sockaddr*)&un, sizeof(un));
|
|
if (rc < 0) {
|
|
perror("bind");
|
|
return false;
|
|
}
|
|
|
|
rc = ::listen(m_fd, 5);
|
|
if (rc < 0) {
|
|
perror("listen");
|
|
return false;
|
|
}
|
|
|
|
m_listening = true;
|
|
setup_notifier();
|
|
return true;
|
|
}
|
|
|
|
RefPtr<LocalSocket> LocalServer::accept()
|
|
{
|
|
VERIFY(m_listening);
|
|
sockaddr_un un;
|
|
socklen_t un_size = sizeof(un);
|
|
int accepted_fd = ::accept(m_fd, (sockaddr*)&un, &un_size);
|
|
if (accepted_fd < 0) {
|
|
perror("accept");
|
|
return nullptr;
|
|
}
|
|
|
|
return LocalSocket::construct(accepted_fd);
|
|
}
|
|
|
|
}
|