mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 23:37:36 +00:00
SystemServer+LibCore: Allow service to request multiple sockets
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.
This commit is contained in:
parent
4316e817db
commit
5b95850e28
6 changed files with 178 additions and 65 deletions
|
@ -50,28 +50,39 @@ LocalServer::~LocalServer()
|
|||
::close(m_fd);
|
||||
}
|
||||
|
||||
bool LocalServer::take_over_from_system_server()
|
||||
bool LocalServer::take_over_from_system_server(String const& socket_path)
|
||||
{
|
||||
if (m_listening)
|
||||
return false;
|
||||
|
||||
constexpr auto socket_takeover = "SOCKET_TAKEOVER";
|
||||
if (!LocalSocket::s_overtaken_sockets_parsed)
|
||||
LocalSocket::parse_sockets_from_system_server();
|
||||
|
||||
if (getenv(socket_takeover)) {
|
||||
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(3, &stat);
|
||||
int rc = fstat(fd, &stat);
|
||||
if (rc == 0 && S_ISSOCK(stat.st_mode)) {
|
||||
// The SystemServer has passed us the socket as fd 3,
|
||||
// so use that instead of creating our own.
|
||||
m_fd = 3;
|
||||
// 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);
|
||||
// We wouldn't want our children to think we're passing
|
||||
// them a socket either, so unset the env variable.
|
||||
unsetenv(socket_takeover);
|
||||
|
||||
m_listening = true;
|
||||
setup_notifier();
|
||||
|
|
|
@ -36,7 +36,7 @@ class LocalServer : public Object {
|
|||
public:
|
||||
virtual ~LocalServer() override;
|
||||
|
||||
bool take_over_from_system_server();
|
||||
bool take_over_from_system_server(String const& path = String());
|
||||
bool is_listening() const { return m_listening; }
|
||||
bool listen(const String& address);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
@ -73,15 +74,49 @@ LocalSocket::~LocalSocket()
|
|||
{
|
||||
}
|
||||
|
||||
RefPtr<LocalSocket> LocalSocket::take_over_accepted_socket_from_system_server()
|
||||
{
|
||||
constexpr auto socket_takeover = "SOCKET_TAKEOVER";
|
||||
if (!getenv(socket_takeover))
|
||||
return nullptr;
|
||||
HashMap<String, int> LocalSocket::s_overtaken_sockets {};
|
||||
bool LocalSocket::s_overtaken_sockets_parsed { false };
|
||||
|
||||
// The SystemServer has passed us the socket as fd 3,
|
||||
// so use that instead of creating our own.
|
||||
constexpr int fd = 3;
|
||||
void LocalSocket::parse_sockets_from_system_server()
|
||||
{
|
||||
VERIFY(!s_overtaken_sockets_parsed);
|
||||
|
||||
constexpr auto socket_takeover = "SOCKET_TAKEOVER";
|
||||
const char* sockets = getenv(socket_takeover);
|
||||
if (!sockets) {
|
||||
s_overtaken_sockets_parsed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& socket : StringView(sockets).split_view(' ')) {
|
||||
auto params = socket.split_view(':');
|
||||
s_overtaken_sockets.set(params[0].to_string(), strtol(params[1].to_string().characters(), nullptr, 10));
|
||||
}
|
||||
|
||||
s_overtaken_sockets_parsed = true;
|
||||
// We wouldn't want our children to think we're passing
|
||||
// them a socket either, so unset the env variable.
|
||||
unsetenv(socket_takeover);
|
||||
}
|
||||
|
||||
RefPtr<LocalSocket> LocalSocket::take_over_accepted_socket_from_system_server(String const& socket_path)
|
||||
{
|
||||
if (!s_overtaken_sockets_parsed)
|
||||
parse_sockets_from_system_server();
|
||||
|
||||
int fd;
|
||||
if (socket_path.is_null()) {
|
||||
// We want the first (and only) socket.
|
||||
VERIFY(s_overtaken_sockets.size() == 1);
|
||||
fd = s_overtaken_sockets.begin()->value;
|
||||
} else {
|
||||
auto it = s_overtaken_sockets.find(socket_path);
|
||||
if (it == s_overtaken_sockets.end()) {
|
||||
dbgln("Non-existent socket requested");
|
||||
return nullptr;
|
||||
}
|
||||
fd = it->value;
|
||||
}
|
||||
|
||||
// Sanity check: it has to be a socket.
|
||||
struct stat stat;
|
||||
|
@ -99,9 +134,6 @@ RefPtr<LocalSocket> LocalSocket::take_over_accepted_socket_from_system_server()
|
|||
// don't need it to be !CLOEXEC anymore, so set the
|
||||
// CLOEXEC flag now.
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
// We wouldn't want our children to think we're passing
|
||||
// them a socket either, so unset the env variable.
|
||||
unsetenv(socket_takeover);
|
||||
return socket;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,11 +35,20 @@ class LocalSocket final : public Socket {
|
|||
public:
|
||||
virtual ~LocalSocket() override;
|
||||
|
||||
static RefPtr<LocalSocket> take_over_accepted_socket_from_system_server();
|
||||
static RefPtr<LocalSocket> take_over_accepted_socket_from_system_server(String const& socket_path = String());
|
||||
|
||||
private:
|
||||
explicit LocalSocket(Object* parent = nullptr);
|
||||
LocalSocket(int fd, Object* parent = nullptr);
|
||||
|
||||
// FIXME: better place to put this so both LocalSocket and LocalServer can
|
||||
// enjoy it?
|
||||
friend class LocalServer;
|
||||
|
||||
static void parse_sockets_from_system_server();
|
||||
|
||||
static HashMap<String, int> s_overtaken_sockets;
|
||||
static bool s_overtaken_sockets_parsed;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue