Commit 11963a9b authored by Martin Flöser's avatar Martin Flöser

Use out-of-band communication between ksld and greeter

The screenlocker_greet needs to tell the parent ksld process which
windows it created. Ksld sends input events to these windows. So
far this was based on an X property on the window. Unfortunately
ksld didn't validate whether the windows tagged with this property
belong to the screenlocker_greet process it started.

With this change the communication for announcing windows is moved
away from the X11 protocol and instead a custom Wayland protocol is
used.

Ksld starts a KWaylandServer when the greet process gets started. It
creates anonymous unix sockets for the connection and passes one
filedescriptor to the started greeter process.

The check for the X property is removed in ksld and instead only
windows ids passed through the Wayland socket connection are
accepted.

REVIEW: 121429
parent 9f49f71c
remove_definitions(-DTRANSLATION_DOMAIN=\"ksmserver\")
add_definitions(-DTRANSLATION_DOMAIN=\"kscreenlocker\")
# adjusting CMAKE_C_FLAGS to get wayland protocols to compile
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu90")
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=1223)
add_subdirectory(kcheckpass)
add_subdirectory(greeter)
......@@ -17,6 +20,7 @@ set(ksld_SRCS
interface.cpp
lockwindow.cpp
logind.cpp
waylandserver.cpp
)
qt5_add_dbus_adaptor(ksld_SRCS ${screensaver_dbusXML} interface.h ScreenLocker::Interface)
qt5_add_dbus_adaptor(ksld_SRCS ${kscreensaver_dbusXML} interface.h ScreenLocker::Interface kscreensaveradaptor KScreenSaverAdaptor)
......@@ -24,6 +28,11 @@ kconfig_add_kcfg_files(ksld_SRCS kcfg/kscreensaversettings.kcfgc)
qt5_add_dbus_interface(ksld_SRCS ${ksmserver_xml} ksmserver_interface)
qt5_add_dbus_interface(ksld_SRCS ${powerdevilpolicyagent_xml} powerdevilpolicyagent)
ecm_add_wayland_server_protocol(ksld_SRCS
PROTOCOL protocols/ksld.xml
BASENAME ksld
)
add_library(screenlocker_static STATIC ${ksld_SRCS})
target_link_libraries(screenlocker_static
......@@ -39,6 +48,8 @@ target_link_libraries(screenlocker_static
${X11_LIBRARIES}
${X11_Xcursor_LIB}
XCB::XCB
KF5::WaylandServer
Wayland::Server
)
# Needed to compile on Arm target.
......
......@@ -18,6 +18,11 @@ set(kscreenlocker_greet_SRCS
qt5_add_resources(kscreenlocker_greet_SRCS fallbacktheme.qrc)
kconfig_add_kcfg_files(kscreenlocker_greet_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../kcfg/kscreensaversettings.kcfgc)
ecm_add_wayland_client_protocol(kscreenlocker_greet_SRCS
PROTOCOL ../protocols/ksld.xml
BASENAME ksld
)
add_executable(kscreenlocker_greet ${kscreenlocker_greet_SRCS})
target_link_libraries(kscreenlocker_greet
......@@ -36,6 +41,8 @@ target_link_libraries(kscreenlocker_greet
Qt5::X11Extras
${X11_LIBRARIES}
KF5::KDELibs4Support
KF5::WaylandClient
Wayland::Client
)
install(TARGETS kscreenlocker_greet DESTINATION ${LIBEXEC_INSTALL_DIR})
......
......@@ -33,10 +33,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <Plasma/Package>
#include <Plasma/PackageStructure>
#include <Plasma/PluginLoader>
// KWayland
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/event_queue.h>
#include <KWayland/Client/registry.h>
// Qt
#include <QtCore/QTimer>
#include <QtGui/QKeyEvent>
#include <qscreen.h>
#include <QThread>
#include <QQuickView>
#include <QQuickItem>
......@@ -45,6 +50,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QQmlProperty>
#include <QX11Info>
// Wayland
#include <wayland-client.h>
#include <wayland-ksld-client-protocol.h>
// X11
#include <X11/Xatom.h>
#include <X11/Xlib.h>
......@@ -77,6 +85,18 @@ UnlockApp::UnlockApp(int &argc, char **argv)
UnlockApp::~UnlockApp()
{
qDeleteAll(m_views);
if (m_ksldInterface) {
org_kde_ksld_destroy(m_ksldInterface);
}
if (m_ksldRegistry) {
delete m_ksldRegistry;
}
if (m_ksldConnection) {
m_ksldConnection->deleteLater();
m_ksldConnectionThread->quit();
m_ksldConnectionThread->wait();
}
}
void UnlockApp::initialize()
......@@ -146,6 +166,12 @@ void UnlockApp::desktopResized()
view->setFlags(Qt::X11BypassWindowManagerHint);
}
if (m_ksldInterface) {
view->create();
org_kde_ksld_x11window(m_ksldInterface, view->winId());
wl_display_flush(m_ksldConnection->display());
}
// engine stuff
QQmlContext* context = view->engine()->rootContext();
const KUser user;
......@@ -390,5 +416,43 @@ void UnlockApp::setNoLock(bool noLock)
m_noLock = noLock;
}
void UnlockApp::setKsldSocket(int socket)
{
using namespace KWayland::Client;
m_ksldConnection = new ConnectionThread;
m_ksldConnection->setSocketFd(socket);
m_ksldRegistry = new Registry();
EventQueue *queue = new EventQueue(m_ksldRegistry);
connect(m_ksldRegistry, &Registry::interfaceAnnounced, this,
[this, queue] (QByteArray interface, quint32 name, quint32 version) {
if (interface != QByteArrayLiteral("org_kde_ksld")) {
return;
}
m_ksldInterface = reinterpret_cast<org_kde_ksld*>(wl_registry_bind(*m_ksldRegistry, name, &org_kde_ksld_interface, version));
queue->addProxy(m_ksldInterface);
for (auto v : m_views) {
org_kde_ksld_x11window(m_ksldInterface, v->winId());
wl_display_flush(m_ksldConnection->display());
}
}
);
connect(m_ksldConnection, &ConnectionThread::connected, this,
[this, queue] {
m_ksldRegistry->create(m_ksldConnection);
queue->setup(m_ksldConnection);
m_ksldRegistry->setEventQueue(queue);
m_ksldRegistry->setup();
wl_display_flush(m_ksldConnection->display());
}, Qt::QueuedConnection);
m_ksldConnectionThread = new QThread(this);
m_ksldConnection->moveToThread(m_ksldConnectionThread);
m_ksldConnectionThread->start();
m_ksldConnection->initConnection();
}
} // namespace
......@@ -29,8 +29,17 @@ namespace Plasma {
class Package;
};
namespace KWayland {
namespace Client {
class ConnectionThread;
class Registry;
}
}
class Authenticator;
struct org_kde_ksld;
namespace ScreenLocker
{
class Unlocker;
......@@ -47,6 +56,7 @@ public:
void lockImmediately();
void setGraceTime(int milliseconds);
void setNoLock(bool noLock);
void setKsldSocket(int socket);
public Q_SLOTS:
void desktopResized();
......@@ -78,6 +88,11 @@ private:
Authenticator *m_authenticator;
int m_graceTime;
bool m_noLock;
KWayland::Client::ConnectionThread *m_ksldConnection = nullptr;
KWayland::Client::Registry *m_ksldRegistry = nullptr;
QThread *m_ksldConnectionThread = nullptr;
org_kde_ksld *m_ksldInterface = nullptr;
};
} // namespace
......
......@@ -80,11 +80,15 @@ int main(int argc, char* argv[])
QStringLiteral("0"));
QCommandLineOption nolockOption(QStringLiteral("nolock"),
i18n("Don't show any lock user interface."));
QCommandLineOption waylandFdOption(QStringLiteral("ksldfd"),
i18n("File descriptor for connecting to ksld."),
QStringLiteral("fd"));
parser.addOption(testingOption);
parser.addOption(immediateLockOption);
parser.addOption(graceTimeOption);
parser.addOption(nolockOption);
parser.addOption(waylandFdOption);
parser.process(app);
if (parser.isSet(testingOption)) {
......@@ -94,11 +98,20 @@ int main(int argc, char* argv[])
app.setImmediateLock(parser.isSet(immediateLockOption));
}
app.setNoLock(parser.isSet(nolockOption));
bool ok = false;
int graceTime = parser.value(graceTimeOption).toInt(&ok);
if (ok) {
app.setGraceTime(graceTime);
}
if (parser.isSet(waylandFdOption)) {
ok = false;
const int fd = parser.value(waylandFdOption).toInt(&ok);
if (ok) {
app.setKsldSocket(fd);
}
}
app.desktopResized();
// This allow ksmserver to know when the applicaion has actually finished setting itself up.
......
......@@ -26,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "logind.h"
#include "kscreensaversettings.h"
#include <config-ksmserver.h>
#include "waylandserver.h"
// workspace
#include <kdisplaymanager.h>
// KDE
......@@ -68,6 +69,7 @@ KSldApp::KSldApp(QObject * parent)
, m_lockState(Unlocked)
, m_lockProcess(NULL)
, m_lockWindow(NULL)
, m_waylandServer(new WaylandServer(this))
, m_lockedTimer(QElapsedTimer())
, m_idleId(0)
, m_lockGrace(0)
......@@ -330,6 +332,7 @@ void KSldApp::doUnlock()
m_lockedTimer.invalidate();
endGraceTime();
KDisplayManager().setLock(false);
m_waylandServer->stop();
emit unlocked();
// KNotification::event( QLatin1String("unlocked"));
}
......@@ -347,12 +350,25 @@ bool KSldApp::startLockProcess(EstablishLock establishLock)
if (m_lockGrace == -1) {
args << "--nolock";
}
// start the Wayland server
int fd = m_waylandServer->start();
if (fd == -1) {
return false;
}
args << "--ksldfd";
args << QString::number(fd);
m_lockProcess->start(QStringLiteral(KSCREENLOCKER_GREET_BIN), args);
// we wait one minute
if (!m_lockProcess->waitForStarted(60000)) {
m_lockProcess->kill();
m_waylandServer->stop();
close(fd);
return false;
}
close(fd);
return true;
}
......@@ -369,6 +385,7 @@ void KSldApp::showLockWindow()
},
Qt::QueuedConnection
);
connect(m_waylandServer, &WaylandServer::x11WindowAdded, m_lockWindow, &LockWindow::addAllowedWindow);
}
m_lockWindow->showLockWindow();
XSync(QX11Info::display(), False);
......
......@@ -40,6 +40,7 @@ enum class EstablishLock {
};
class LockWindow;
class WaylandServer;
class KSldApp : public QObject
{
......@@ -104,6 +105,7 @@ private:
LockState m_lockState;
QProcess *m_lockProcess;
LockWindow *m_lockWindow;
WaylandServer *m_waylandServer;
/**
* Timer to measure how long the screen is locked.
* This information is required by DBus Interface.
......
......@@ -158,6 +158,7 @@ void LockWindow::hideLockWindow()
gVRoot = 0;
}
XSync(QX11Info::display(), False);
m_allowedWindows.clear();
}
//---------------------------------------------------------------------------
......@@ -370,7 +371,7 @@ bool LockWindow::nativeEventFilter(const QByteArray &eventType, void *message, l
m_windowInfo[ index ].viewable = true;
else
qDebug() << "Unknown toplevel for MapNotify";
if (isLockWindow(xm->window)) {
if (m_allowedWindows.contains(xm->window)) {
if (m_lockWindows.contains(xm->window)) {
qDebug() << "uhoh! duplicate!";
} else {
......@@ -497,28 +498,6 @@ void LockWindow::stayOnTop()
XRestackWindows( QX11Info::display(), stack.data(), count );
}
bool LockWindow::isLockWindow(Window id)
{
Atom tag = XInternAtom(QX11Info::display(), "_KDE_SCREEN_LOCKER", False);
Atom actualType;
int actualFormat;
unsigned long nitems, remaining;
unsigned char *data = 0;
Display *display = QX11Info::display();
int result = XGetWindowProperty(display, id, tag, 0, 1, False, tag, &actualType,
&actualFormat, &nitems, &remaining, &data);
bool lockWindow = false;
if (result == Success && actualType == tag) {
lockWindow = true;
}
if (data) {
XFree(data);
}
return lockWindow;
}
void LockWindow::updateGeo()
{
QDesktopWidget *desktop = QApplication::desktop();
......@@ -530,6 +509,29 @@ void LockWindow::paintEvent(QPaintEvent* )
QPainter p(this);
p.setBrush(QBrush(Qt::black));
p.drawRect(geometry());
stayOnTop();
}
void LockWindow::addAllowedWindow(quint32 window)
{
m_allowedWindows << window;
// test whether it's to show
const int index = findWindowInfo( window );
if (index == -1 || !m_windowInfo[ index ].viewable) {
return;
}
if (m_lockWindows.contains(window)) {
qDebug() << "uhoh! duplicate!";
} else {
if (!isVisible()) {
// not yet shown and we have a lock window, so we show our own window
show();
setCursor(Qt::ArrowCursor);
}
m_lockWindows.prepend(window);
fakeFocusIn(window);
stayOnTop();
}
}
}
......@@ -41,6 +41,8 @@ public:
void showLockWindow();
void hideLockWindow();
void addAllowedWindow(quint32 window);
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
Q_SIGNALS:
......@@ -59,7 +61,6 @@ private:
void removeVRoot(Window win);
int findWindowInfo(Window w);
void stayOnTop();
bool isLockWindow(Window w);
struct WindowInfo
{
Window window;
......@@ -67,6 +68,7 @@ private:
};
QList<WindowInfo> m_windowInfo;
QList<WId> m_lockWindows;
QList<quint32> m_allowedWindows;
};
}
......
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ksld">
<interface name="org_kde_ksld" version="1">
<request name="x11window">
<arg name="id" type="uint"/>
</request>
</interface>
</protocol>
/********************************************************************
KSld - the KDE Screenlocker Daemon
This file is part of the KDE project.
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "waylandserver.h"
// ksld
#include <config-ksmserver.h>
#include <wayland-ksld-server-protocol.h>
// KWayland
#include <KWayland/Server/display.h>
// Wayland
#include <wayland-server.h>
// system
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
namespace ScreenLocker
{
WaylandServer::WaylandServer(QObject *parent)
: QObject(parent)
{
}
WaylandServer::~WaylandServer()
{
stop();
}
int WaylandServer::start()
{
stop();
m_display.reset(new KWayland::Server::Display);
m_display->start(KWayland::Server::Display::StartMode::ConnectClientsOnly);
if (!m_display->isRunning()) {
// failed to start the Wayland server
return -1;
}
int socketPair[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair) == -1) {
// failed creating socket
return -1;
}
fcntl(socketPair[0], F_SETFD, FD_CLOEXEC);
m_allowedClient = m_display->createClient(socketPair[0]);
if (!m_allowedClient) {
// failed creating the Wayland client
stop();
close(socketPair[0]);
close(socketPair[1]);
return -1;
}
m_interface = wl_global_create(*m_display.data(), &org_kde_ksld_interface, 1, this, bind);
return socketPair[1];
}
void WaylandServer::stop()
{
if (m_interface) {
wl_global_destroy(m_interface);
m_interface = nullptr;
}
m_display.reset();
m_allowedClient = nullptr;
}
void WaylandServer::bind(wl_client *client, void *data, uint32_t version, uint32_t id)
{
auto s = reinterpret_cast<WaylandServer*>(data);
if (client != s->m_allowedClient->client()) {
// a proper error would be better
wl_client_post_no_memory(client);
return;
}
wl_resource *r = s->m_allowedClient->createResource(&org_kde_ksld_interface, qMin(version, 1u), id);
if (!r) {
wl_client_post_no_memory(client);
return;
}
static const struct org_kde_ksld_interface s_interface = {
x11WindowCallback
};
wl_resource_set_implementation(r, &s_interface, s, unbind);
s->m_allowedClient->flush();
}
void WaylandServer::unbind(wl_resource *resource)
{
Q_UNUSED(resource)
}
void WaylandServer::x11WindowCallback(wl_client *client, wl_resource *resource, uint32_t id)
{
auto s = reinterpret_cast<WaylandServer*>(wl_resource_get_user_data(resource));
if (s->m_allowedClient->client() != client) {
return;
}
emit s->x11WindowAdded(id);
}
}
/********************************************************************
KSld - the KDE Screenlocker Daemon
This file is part of the KDE project.
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef SCREENLOCKER_WAYLANDSERVER_H
#define SCREENLOCKER_WAYLANDSERVER_H
#include <QObject>
struct wl_client;
struct wl_global;
struct wl_resource;
namespace KWayland
{
namespace Server
{
class ClientConnection;
class Display;
}
}
namespace ScreenLocker
{
class WaylandServer : public QObject
{
Q_OBJECT
public:
explicit WaylandServer(QObject *parent = nullptr);
virtual ~WaylandServer();
int start();
void stop();
Q_SIGNALS:
void x11WindowAdded(quint32 window);
private:
static void bind(wl_client *client, void *data, uint32_t version, uint32_t id);
static void unbind(wl_resource *resource);
static void x11WindowCallback(wl_client *client, wl_resource *resource, uint32_t id);
QScopedPointer<KWayland::Server::Display> m_display;
KWayland::Server::ClientConnection *m_allowedClient = nullptr;
wl_global *m_interface = nullptr;
};
}
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment