File indexing completed on 2024-04-21 05:27:35

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