File indexing completed on 2022-05-16 17:44:53

0001 /********************************************************************
0002  KSld - the KDE Screenlocker Daemon
0003  This file is part of the KDE project.
0004 
0005 Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007 This program is free software; you can redistribute it and/or modify
0008 it under the terms of the GNU General Public License as published by
0009 the Free Software Foundation; either version 2 of the License, or
0010 (at your option) any later version.
0011 
0012 This program is distributed in the hope that it will be useful,
0013 but WITHOUT ANY WARRANTY; without even the implied warranty of
0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015 GNU General Public License for more details.
0016 
0017 You should have received a copy of the GNU General Public License
0018 along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019 *********************************************************************/
0020 #include "logind.h"
0021 
0022 #include <KLocalizedString>
0023 
0024 #include <QCoreApplication>
0025 #include <QDBusConnectionInterface>
0026 #include <QDBusServiceWatcher>
0027 #include <QStandardPaths>
0028 
0029 #include <kscreenlocker_logging.h>
0030 
0031 const static QString s_login1Service = QStringLiteral("org.freedesktop.login1");
0032 const static QString s_login1Path = QStringLiteral("/org/freedesktop/login1");
0033 const static QString s_login1ManagerInterface = QStringLiteral("org.freedesktop.login1.Manager");
0034 const static QString s_login1SessionInterface = QStringLiteral("org.freedesktop.login1.Session");
0035 
0036 const static QString s_consolekitService = QStringLiteral("org.freedesktop.ConsoleKit");
0037 const static QString s_consolekitPath = QStringLiteral("/org/freedesktop/ConsoleKit/Manager");
0038 const static QString s_consolekitManagerInterface = QStringLiteral("org.freedesktop.ConsoleKit.Manager");
0039 const static QString s_consolekitSessionInterface = QStringLiteral("org.freedesktop.ConsoleKit.Session");
0040 
0041 const static QString s_propertyInterface = QStringLiteral("org.freedesktop.DBus.Properties");
0042 
0043 LogindIntegration::LogindIntegration(const QDBusConnection &connection, QObject *parent)
0044     : QObject(parent)
0045     , m_bus(connection)
0046     , m_logindServiceWatcher(
0047           new QDBusServiceWatcher(s_login1Service, m_bus, QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration, this))
0048     , m_connected(false)
0049     , m_inhibitFileDescriptor()
0050     , m_service(nullptr)
0051     , m_path(nullptr)
0052     , m_managerInterface(nullptr)
0053     , m_sessionInterface(nullptr)
0054 {
0055     // if we are inside Kwin's unit tests don't query or manipulate logind
0056     // avoid connecting but keep all stubs in place
0057     if (QStandardPaths::isTestModeEnabled()) {
0058         return;
0059     }
0060 
0061     connect(m_logindServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &LogindIntegration::logindServiceRegistered);
0062     connect(m_logindServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this]() {
0063         m_connected = false;
0064         m_sessionPath = QString();
0065         Q_EMIT connectedChanged();
0066     });
0067 
0068     // check whether the logind service is registered
0069     QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
0070                                                           QStringLiteral("/"),
0071                                                           QStringLiteral("org.freedesktop.DBus"),
0072                                                           QStringLiteral("ListNames"));
0073     QDBusPendingReply<QStringList> async = m_bus.asyncCall(message);
0074     QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
0075     connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) {
0076         QDBusPendingReply<QStringList> reply = *self;
0077         self->deleteLater();
0078         if (!reply.isValid()) {
0079             return;
0080         }
0081         if (reply.value().contains(s_login1Service)) {
0082             logindServiceRegistered();
0083             // Don't register ck if we have logind
0084             return;
0085         }
0086         if (reply.value().contains(s_consolekitService)) {
0087             consolekitServiceRegistered();
0088         }
0089     });
0090 }
0091 
0092 LogindIntegration::LogindIntegration(QObject *parent)
0093     : LogindIntegration(QDBusConnection::systemBus(), parent)
0094 {
0095 }
0096 
0097 LogindIntegration::~LogindIntegration() = default;
0098 
0099 void LogindIntegration::logindServiceRegistered()
0100 {
0101     // get the current session
0102     QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, s_login1Path, s_login1ManagerInterface, QStringLiteral("GetSession"));
0103     message.setArguments({QStringLiteral("auto")});
0104     QDBusPendingReply<QDBusObjectPath> session = m_bus.asyncCall(message);
0105     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(session, this);
0106 
0107     m_service = &s_login1Service;
0108     m_path = &s_login1Path;
0109     m_managerInterface = &s_login1ManagerInterface;
0110     m_sessionInterface = &s_login1SessionInterface;
0111 
0112     commonServiceRegistered(watcher);
0113 }
0114 
0115 void LogindIntegration::consolekitServiceRegistered()
0116 {
0117     // Don't try to register with ck if we have logind
0118     if (m_connected) {
0119         return;
0120     }
0121 
0122     // get the current session
0123     QDBusMessage message =
0124         QDBusMessage::createMethodCall(s_consolekitService, s_consolekitPath, s_consolekitManagerInterface, QStringLiteral("GetCurrentSession"));
0125     QDBusPendingReply<QDBusObjectPath> session = m_bus.asyncCall(message);
0126     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(session, this);
0127 
0128     m_service = &s_consolekitService;
0129     m_path = &s_consolekitPath;
0130     m_managerInterface = &s_consolekitManagerInterface;
0131     m_sessionInterface = &s_consolekitSessionInterface;
0132 
0133     commonServiceRegistered(watcher);
0134 }
0135 
0136 void LogindIntegration::commonServiceRegistered(QDBusPendingCallWatcher *watcher)
0137 {
0138     connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) {
0139         QDBusPendingReply<QDBusObjectPath> reply = *self;
0140         self->deleteLater();
0141         if (m_connected) {
0142             return;
0143         }
0144         if (!reply.isValid()) {
0145             qCDebug(KSCREENLOCKER) << "The session is not registered: " << reply.error().message();
0146             return;
0147         }
0148         m_sessionPath = reply.value().path();
0149         qCDebug(KSCREENLOCKER) << "Session path:" << m_sessionPath;
0150 
0151         // connections need to be done this way as the object exposes both method and signal
0152         // with name "Lock"/"Unlock". Qt is not able to automatically handle this.
0153         m_bus.connect(*m_service, m_sessionPath, *m_sessionInterface, QStringLiteral("Lock"), this, SIGNAL(requestLock()));
0154         m_bus.connect(*m_service, m_sessionPath, *m_sessionInterface, QStringLiteral("Unlock"), this, SIGNAL(requestUnlock()));
0155         m_connected = true;
0156         Q_EMIT connectedChanged();
0157     });
0158 
0159     // connect to manager object's signals we need
0160     m_bus.connect(*m_service, *m_path, *m_managerInterface, QStringLiteral("PrepareForSleep"), this, SIGNAL(prepareForSleep(bool)));
0161 }
0162 
0163 void LogindIntegration::inhibit()
0164 {
0165     if (m_inhibitFileDescriptor.isValid()) {
0166         return;
0167     }
0168 
0169     if (!m_connected) {
0170         return;
0171     }
0172 
0173     QDBusMessage message = QDBusMessage::createMethodCall(*m_service, *m_path, *m_managerInterface, QStringLiteral("Inhibit"));
0174     message.setArguments(QVariantList(
0175         {QStringLiteral("sleep"), i18n("Screen Locker"), i18n("Ensuring that the screen gets locked before going to sleep"), QStringLiteral("delay")}));
0176     QDBusPendingReply<QDBusUnixFileDescriptor> reply = m_bus.asyncCall(message);
0177     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
0178     connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) {
0179         QDBusPendingReply<QDBusUnixFileDescriptor> reply = *self;
0180         self->deleteLater();
0181         if (!reply.isValid()) {
0182             return;
0183         }
0184         reply.value().swap(m_inhibitFileDescriptor);
0185         Q_EMIT inhibited();
0186     });
0187 }
0188 
0189 void LogindIntegration::uninhibit()
0190 {
0191     if (!m_inhibitFileDescriptor.isValid()) {
0192         return;
0193     }
0194     m_inhibitFileDescriptor = QDBusUnixFileDescriptor();
0195 }
0196 
0197 bool LogindIntegration::isInhibited() const
0198 {
0199     return m_inhibitFileDescriptor.isValid();
0200 }
0201 
0202 void LogindIntegration::setLocked(bool locked)
0203 {
0204     if (!m_connected || m_sessionPath.isEmpty()) {
0205         return;
0206     }
0207 
0208     QDBusMessage message = QDBusMessage::createMethodCall(*m_service, m_sessionPath, *m_sessionInterface, QStringLiteral("SetLockedHint"));
0209     message.setArguments({locked});
0210     m_bus.call(message, QDBus::NoBlock);
0211 }
0212 
0213 bool LogindIntegration::isLocked() const
0214 {
0215     if (!m_connected || m_sessionPath.isEmpty()) {
0216         return false;
0217     }
0218 
0219     QDBusMessage message = QDBusMessage::createMethodCall(*m_service, m_sessionPath, s_propertyInterface, QStringLiteral("Get"));
0220     message.setArguments({*m_sessionInterface, QStringLiteral("LockedHint")});
0221     QDBusReply<QDBusVariant> reply = m_bus.call(message);
0222     if (reply.isValid()) {
0223         return reply.value().variant().toBool();
0224     }
0225     qCDebug(KSCREENLOCKER()) << reply.error();
0226     return false;
0227 }