File indexing completed on 2024-04-28 05:35:36

0001 /*
0002     SPDX-FileCopyrightText: 2019 David Edmundson <davidedmundson@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "sessionmanagementbackend.h"
0008 
0009 #include <QDebug>
0010 #include <QMutex>
0011 #include <QMutexLocker>
0012 
0013 #include <QDBusConnection>
0014 #include <QDBusConnectionInterface>
0015 #include <QDBusPendingCallWatcher>
0016 
0017 #include <KConfigGroup>
0018 #include <iostream>
0019 
0020 #include "login1_manager_interface.h"
0021 
0022 static SessionBackend *s_backend = nullptr;
0023 
0024 SessionBackend *SessionBackend::self()
0025 {
0026     static QMutex mutex;
0027     QMutexLocker lock(&mutex);
0028 
0029     if (s_backend) {
0030         return s_backend;
0031     }
0032 
0033     if (qEnvironmentVariableIntValue("PLASMA_SESSION_GUI_TEST")) {
0034         s_backend = new TestSessionBackend();
0035     } else if (LogindSessionBackend::exists()) {
0036         s_backend = new LogindSessionBackend();
0037     } else {
0038         s_backend = new DummySessionBackend();
0039     }
0040 
0041     return s_backend;
0042 }
0043 
0044 SessionBackend::SessionBackend()
0045     : m_kserverConfig(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("ksmserverrc"))))
0046 {
0047 }
0048 
0049 bool SessionBackend::confirmLogout() const
0050 {
0051     return m_kserverConfig->config()->group(QStringLiteral("General")).readEntry("confirmLogout", true);
0052 }
0053 
0054 bool SessionBackend::canSwitchUser() const
0055 {
0056     return false;
0057 }
0058 
0059 DummySessionBackend::DummySessionBackend()
0060 {
0061     qCritical() << "Could not load a session backend. Session management operations such as shutdown will not be operational. This is a setup issue.";
0062 }
0063 
0064 TestSessionBackend::TestSessionBackend()
0065 {
0066     qWarning() << "This backend is intended for gui autotesting only, it will not be operational";
0067 }
0068 
0069 void TestSessionBackend::shutdown()
0070 {
0071     std::cout << "shutdown" << std::endl;
0072 }
0073 
0074 void TestSessionBackend::reboot()
0075 {
0076     std::cout << "reboot" << std::endl;
0077 }
0078 
0079 void TestSessionBackend::suspend()
0080 {
0081     std::cout << "suspend" << std::endl;
0082 }
0083 
0084 void TestSessionBackend::hybridSuspend()
0085 {
0086     std::cout << "hybridSuspend" << std::endl;
0087 }
0088 
0089 void TestSessionBackend::hibernate()
0090 {
0091     std::cout << "hibernate" << std::endl;
0092 }
0093 
0094 void TestSessionBackend::suspendThenHibernate()
0095 {
0096     std::cout << "suspendThenHibernate" << std::endl;
0097 }
0098 
0099 /*********************************************************************************/
0100 
0101 LogindSessionBackend::LogindSessionBackend()
0102 {
0103     m_login1 = new OrgFreedesktopLogin1ManagerInterface(QStringLiteral("org.freedesktop.login1"),
0104                                                         QStringLiteral("/org/freedesktop/login1"),
0105                                                         QDBusConnection::systemBus(),
0106                                                         this);
0107 
0108     auto propLoaded = [this](QDBusPendingCallWatcher *watcher, bool *argToUpdate) {
0109         watcher->deleteLater();
0110         m_pendingJobs--;
0111         QDBusPendingReply<QString> reply = *watcher;
0112         if (reply.isError()) {
0113             *argToUpdate = false;
0114         } else {
0115             // both "yes" and "challenge" will show up in the UI
0116             const QString value = reply.value();
0117             *argToUpdate = false;
0118             if (value == QLatin1String("yes") || value == QLatin1String("challenge")) {
0119                 *argToUpdate = true;
0120             }
0121         }
0122 
0123         if (m_pendingJobs == 0) {
0124             m_state = SessionManagement::State::Ready;
0125             Q_EMIT stateChanged();
0126             Q_EMIT canShutdownChanged();
0127             Q_EMIT canRebootChanged();
0128             Q_EMIT canSuspendChanged();
0129             Q_EMIT canHibernateChanged();
0130             Q_EMIT canSuspendThenHibernateChanged();
0131         }
0132     };
0133 
0134     m_pendingJobs = 5;
0135     {
0136         auto watcher = new QDBusPendingCallWatcher(m_login1->CanPowerOff(), this);
0137         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canShutdown));
0138     }
0139     {
0140         auto watcher = new QDBusPendingCallWatcher(m_login1->CanReboot(), this);
0141         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canReboot));
0142     }
0143     {
0144         auto watcher = new QDBusPendingCallWatcher(m_login1->CanSuspend(), this);
0145         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canSuspend));
0146     }
0147     {
0148         auto watcher = new QDBusPendingCallWatcher(m_login1->CanHybridSleep(), this);
0149         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canHybridSuspend));
0150     }
0151     {
0152         auto watcher = new QDBusPendingCallWatcher(m_login1->CanHibernate(), this);
0153         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canHibernate));
0154     }
0155     {
0156         auto watcher = new QDBusPendingCallWatcher(m_login1->CanSuspendThenHibernate(), this);
0157         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canSuspendThenHibernate));
0158     }
0159 
0160     connect(m_login1, &OrgFreedesktopLogin1ManagerInterface::PrepareForSleep, this, [this](bool sleeping) {
0161         if (sleeping) {
0162             Q_EMIT aboutToSuspend();
0163         } else {
0164             Q_EMIT resumingFromSuspend();
0165         }
0166     });
0167 }
0168 
0169 SessionManagement::State LogindSessionBackend::state() const
0170 {
0171     return m_state;
0172 }
0173 
0174 void LogindSessionBackend::shutdown()
0175 {
0176     // logind will confirm credentials with the caller, if the app quits after sending this
0177     // this may fail
0178     m_login1->PowerOff(true).waitForFinished();
0179 }
0180 
0181 void LogindSessionBackend::reboot()
0182 {
0183     m_login1->Reboot(true).waitForFinished();
0184 }
0185 
0186 void LogindSessionBackend::suspend()
0187 {
0188     // these need to be synchronous as well - ksmserver-logout-greeter specifically calls these
0189     // and will quit immediately after
0190     m_login1->Suspend(true).waitForFinished();
0191 }
0192 
0193 void LogindSessionBackend::hybridSuspend()
0194 {
0195     m_login1->HybridSleep(true).waitForFinished();
0196 }
0197 
0198 void LogindSessionBackend::hibernate()
0199 {
0200     m_login1->Hibernate(true).waitForFinished();
0201 }
0202 
0203 void LogindSessionBackend::suspendThenHibernate()
0204 {
0205     m_login1->SuspendThenHibernate(true).waitForFinished();
0206 }
0207 
0208 bool LogindSessionBackend::canShutdown() const
0209 {
0210     return m_canShutdown;
0211 }
0212 
0213 bool LogindSessionBackend::canReboot() const
0214 {
0215     return m_canReboot;
0216 }
0217 
0218 bool LogindSessionBackend::canSuspend() const
0219 {
0220     return m_canSuspend;
0221 }
0222 
0223 bool LogindSessionBackend::canHybridSuspend() const
0224 {
0225     return m_canHybridSuspend;
0226 }
0227 
0228 bool LogindSessionBackend::canHibernate() const
0229 {
0230     return m_canHibernate;
0231 }
0232 
0233 bool LogindSessionBackend::canSuspendThenHibernate() const
0234 {
0235     return m_canSuspendThenHibernate;
0236 }
0237 
0238 bool LogindSessionBackend::canSwitchUser() const
0239 {
0240     return true;
0241 }