commit e6c7e90f15c4bf62a9d0c1aa155c67b9648b4d14 Author: Martin Gräßlin <mgraesslin@kde.org> Date: Mon Nov 17 16:01:18 2014 +0100 Adding a new ClientConnection class for wrapping wl_client The ClientConnection is managed by Display. Whenever one tries to get a ClientConnection for a wl_client* and it doesn't exist yet a new one will be created and a clientConnected signal will be emitted. Also there is a clientDisconnected signal. ClientConnection provides access to pid, uid and gid. The idea is to extend it to provide access to all the resources created for the client. diff --git a/autotests/server/test_display.cpp b/autotests/server/test_display.cpp index 2c257c1..34becff 100644 --- a/autotests/server/test_display.cpp +++ b/autotests/server/test_display.cpp @@ -21,7 +21,14 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. #include <QtTest/QtTest> // WaylandServer #include "../../src/server/display.h" +#include "../../src/server/clientconnection.h" #include "../../src/server/output_interface.h" +// Wayland +#include <wayland-server.h> +// system +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> using namespace KWayland::Server; @@ -32,6 +39,7 @@ private Q_SLOTS: void testSocketName(); void testStartStop(); void testAddRemoveOutput(); + void testClientConnection(); }; void TestWaylandServerDisplay::testSocketName() @@ -101,5 +109,61 @@ void TestWaylandServerDisplay::testAddRemoveOutput() QVERIFY(display.outputs().isEmpty()); } +void TestWaylandServerDisplay::testClientConnection() +{ + Display display; + display.setSocketName(QStringLiteral("kwin-wayland-server-display-test-client-connection")); + display.start(); + QSignalSpy connectedSpy(&display, SIGNAL(clientConnected(KWayland::Server::ClientConnection*))); + QVERIFY(connectedSpy.isValid()); + QSignalSpy disconnectedSpy(&display, SIGNAL(clientDisconnected(KWayland::Server::ClientConnection*))); + QVERIFY(disconnectedSpy.isValid()); + + int sv[2]; + QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); + + auto client = wl_client_create(display, sv[0]); + QVERIFY(client); + + QVERIFY(connectedSpy.isEmpty()); + ClientConnection *connection = display.getConnection(client); + QVERIFY(connection); + QCOMPARE(connection->client(), client); + QVERIFY(connection->userId() != 0); + QVERIFY(connection->groupId() != 0); + QVERIFY(connection->processId() != 0); + QCOMPARE((wl_client*)*connection, client); + const ClientConnection &constRef = *connection; + QCOMPARE((wl_client*)constRef, client); + QCOMPARE(connectedSpy.count(), 1); + QCOMPARE(connectedSpy.first().first().value<ClientConnection*>(), connection); + + QCOMPARE(connection, display.getConnection(client)); + QCOMPARE(connectedSpy.count(), 1); + + // create a second client + int sv2[2]; + QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv2) >= 0); + auto client2 = wl_client_create(display, sv2[0]); + QVERIFY(client2); + ClientConnection *connection2 = display.getConnection(client2); + QVERIFY(connection2); + QCOMPARE(connection2->client(), client2); + QCOMPARE(connectedSpy.count(), 2); + QCOMPARE(connectedSpy.first().first().value<ClientConnection*>(), connection); + QCOMPARE(connectedSpy.last().first().value<ClientConnection*>(), connection2); + + // and destroy + QVERIFY(disconnectedSpy.isEmpty()); + wl_client_destroy(client); + QCOMPARE(disconnectedSpy.count(), 1); + wl_client_destroy(client2); + QCOMPARE(disconnectedSpy.count(), 2); + close(sv[0]); + close(sv[1]); + close(sv2[0]); + close(sv2[1]); +} + QTEST_GUILESS_MAIN(TestWaylandServerDisplay) #include "test_display.moc" diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index e3f58cc..bd64770 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,5 +1,6 @@ set(SERVER_LIB_SRCS buffer_interface.cpp + clientconnection.cpp compositor_interface.cpp display.cpp output_interface.cpp @@ -50,6 +51,7 @@ set_target_properties(KF5WaylandServer PROPERTIES VERSION ${KWAYLAND_VERSION_S install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kwaylandserver_export.h buffer_interface.h + clientconnection.h compositor_interface.h display.h output_interface.h diff --git a/src/server/clientconnection.cpp b/src/server/clientconnection.cpp new file mode 100644 index 0000000..502db67 --- /dev/null +++ b/src/server/clientconnection.cpp @@ -0,0 +1,129 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin <mgraesslin@kde.org> + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see <http://www.gnu.org/licenses/>. +*********************************************************************/ +#include "clientconnection.h" +#include "display.h" +// Qt +#include <QVector> +// Wayland +#include <wayland-server.h> + +namespace KWayland +{ +namespace Server +{ + +class ClientConnection::Private +{ +public: + explicit Private(wl_client *c, Display *display, ClientConnection *q); + ~Private(); + + wl_client *client; + Display *display; + pid_t pid = 0; + uid_t user = 0; + gid_t group = 0; + +private: + static void destroyListenerCallback(wl_listener *listener, void *data); + ClientConnection *q; + wl_listener listener; + static QVector<Private*> s_allClients; +}; + +QVector<ClientConnection::Private*> ClientConnection::Private::s_allClients; + +ClientConnection::Private::Private(wl_client *c, Display *display, ClientConnection *q) + : client(c) + , display(display) + , q(q) +{ + s_allClients << this; + listener.notify = destroyListenerCallback; + wl_client_add_destroy_listener(c, &listener); + wl_client_get_credentials(client, &pid, &user, &group); +} + +ClientConnection::Private::~Private() +{ + wl_list_remove(&listener.link); + s_allClients.removeAt(s_allClients.indexOf(this)); +} + +void ClientConnection::Private::destroyListenerCallback(wl_listener *listener, void *data) +{ + Q_UNUSED(listener) + wl_client *client = reinterpret_cast<wl_client*>(data); + auto it = std::find_if(s_allClients.constBegin(), s_allClients.constEnd(), + [client](Private *c) { + return c->client == client; + } + ); + Q_ASSERT(it != s_allClients.constEnd()); + auto q = (*it)->q; + emit q->disconnected(q); + delete q; +} + +ClientConnection::ClientConnection(wl_client *c, Display *parent) + : QObject(parent) + , d(new Private(c, parent, this)) +{ +} + +ClientConnection::~ClientConnection() = default; + +wl_client *ClientConnection::client() +{ + return d->client; +} + +ClientConnection::operator wl_client*() +{ + return d->client; +} + +ClientConnection::operator wl_client*() const +{ + return d->client; +} + +Display *ClientConnection::display() +{ + return d->display; +} + +gid_t ClientConnection::groupId() const +{ + return d->group; +} + +pid_t ClientConnection::processId() const +{ + return d->pid; +} + +uid_t ClientConnection::userId() const +{ + return d->user; +} + +} +} diff --git a/src/server/clientconnection.h b/src/server/clientconnection.h new file mode 100644 index 0000000..5a90eea --- /dev/null +++ b/src/server/clientconnection.h @@ -0,0 +1,67 @@ +/******************************************************************** +Copyright 2014 Martin Gräßlin <mgraesslin@kde.org> + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see <http://www.gnu.org/licenses/>. +*********************************************************************/ +#ifndef KWAYLAND_SERVER_CLIENTCONNECTION_H +#define KWAYLAND_SERVER_CLIENTCONNECTION_H + +#include <QObject> + +#include <kwaylandserver_export.h> + +struct wl_client; + +namespace KWayland +{ +namespace Server +{ + +class Display; + +class KWAYLANDSERVER_EXPORT ClientConnection : public QObject +{ + Q_OBJECT +public: + virtual ~ClientConnection(); + + wl_client *client(); + Display *display(); + + pid_t processId() const; + uid_t userId() const; + gid_t groupId() const; + + operator wl_client*(); + operator wl_client*() const; + +Q_SIGNALS: + void disconnected(KWayland::Server::ClientConnection*); + +private: + friend class Display; + explicit ClientConnection(wl_client *c, Display *parent); + class Private; + QScopedPointer<Private> d; +}; + +} +} + +Q_DECLARE_METATYPE(KWayland::Server::ClientConnection*) + +#endif diff --git a/src/server/display.cpp b/src/server/display.cpp index 350395f..25df978 100644 --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -50,6 +50,7 @@ public: QString socketName = QStringLiteral("wayland-0"); bool running = false; QList<OutputInterface*> outputs; + QVector<ClientConnection*> clients; private: Display *q; @@ -246,6 +247,32 @@ QList< OutputInterface* > Display::outputs() const return d->outputs; } +ClientConnection *Display::getConnection(wl_client *client) +{ + Q_ASSERT(client); + auto it = std::find_if(d->clients.constBegin(), d->clients.constEnd(), + [client](ClientConnection *c) { + return c->client() == client; + } + ); + if (it != d->clients.constEnd()) { + return *it; + } + // no ConnectionData yet, create it + auto c = new ClientConnection(client, this); + d->clients << c; + connect(c, &ClientConnection::disconnected, this, + [this] (ClientConnection *c) { + const int index = d->clients.indexOf(c); + Q_ASSERT(index != -1); + d->clients.remove(index); + Q_ASSERT(d->clients.indexOf(c) == -1); + emit clientDisconnected(c); + } + ); + emit clientConnected(c); + return c; +} } } diff --git a/src/server/display.h b/src/server/display.h index 36b8d5d..88d4f43 100644 --- a/src/server/display.h +++ b/src/server/display.h @@ -25,6 +25,9 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. #include <kwaylandserver_export.h> +#include "clientconnection.h" + +struct wl_client; struct wl_display; struct wl_event_loop; @@ -89,10 +92,20 @@ public: ShellInterface *createShell(QObject *parent = nullptr); SeatInterface *createSeat(QObject *parent = nullptr); + /** + * Gets the ClientConnection for the given @p client. + * If there is no ClientConnection yet for the given @p client, it will be created. + * @param client The native client for which the ClientConnection is retrieved + * @return The ClientConnection for the given native client + **/ + ClientConnection *getConnection(wl_client *client); + Q_SIGNALS: void socketNameChanged(const QString&); void runningChanged(bool); void aboutToTerminate(); + void clientConnected(KWayland::Server::ClientConnection*); + void clientDisconnected(KWayland::Server::ClientConnection*); private: class Private;