File indexing completed on 2024-04-28 16:54:32

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 
0019 #include "kdisplaymanager.h"
0020 
0021 #include "consolekit_manager_interface.h"
0022 #include "login1_manager_interface.h"
0023 #include "upower_interface.h"
0024 
0025 static SessionBackend *s_backend = nullptr;
0026 
0027 SessionBackend *SessionBackend::self()
0028 {
0029     static QMutex mutex;
0030     QMutexLocker lock(&mutex);
0031 
0032     if (s_backend) {
0033         return s_backend;
0034     }
0035 
0036     if (qEnvironmentVariableIntValue("PLASMA_SESSION_GUI_TEST")) {
0037         s_backend = new TestSessionBackend();
0038     } else if (LogindSessionBackend::exists()) {
0039         s_backend = new LogindSessionBackend();
0040     } else if (ConsoleKitSessionBackend::exists()) {
0041         s_backend = new ConsoleKitSessionBackend();
0042     } else {
0043         s_backend = new DummySessionBackend();
0044     }
0045 
0046     return s_backend;
0047 }
0048 
0049 SessionBackend::SessionBackend()
0050     : m_kserverConfig(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("ksmserverrc"))))
0051 {
0052 }
0053 
0054 bool SessionBackend::confirmLogout() const
0055 {
0056     return m_kserverConfig->config()->group("General").readEntry("confirmLogout", true);
0057 }
0058 
0059 bool SessionBackend::canSwitchUser() const
0060 {
0061     return KDisplayManager().isSwitchable();
0062 }
0063 
0064 DummySessionBackend::DummySessionBackend()
0065 {
0066     qCritical() << "Could not load a session backend. Session management operations such as shutdown will not be operational. This is a setup issue.";
0067 }
0068 
0069 TestSessionBackend::TestSessionBackend()
0070 {
0071     qWarning() << "This backend is intended for gui autotesting only, it will not be operational";
0072 }
0073 
0074 void TestSessionBackend::shutdown()
0075 {
0076     qWarning() << "shutdown";
0077 }
0078 
0079 void TestSessionBackend::reboot()
0080 {
0081     qWarning() << "reboot";
0082 }
0083 
0084 void TestSessionBackend::suspend()
0085 {
0086     qWarning() << "suspend";
0087 }
0088 
0089 void TestSessionBackend::hybridSuspend()
0090 {
0091     qWarning() << "hybridSuspend";
0092 }
0093 
0094 void TestSessionBackend::hibernate()
0095 {
0096     qWarning() << "hibernate";
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         }
0131     };
0132 
0133     m_pendingJobs = 5;
0134     {
0135         auto watcher = new QDBusPendingCallWatcher(m_login1->CanPowerOff(), this);
0136         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canShutdown));
0137     }
0138     {
0139         auto watcher = new QDBusPendingCallWatcher(m_login1->CanReboot(), this);
0140         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canReboot));
0141     }
0142     {
0143         auto watcher = new QDBusPendingCallWatcher(m_login1->CanSuspend(), this);
0144         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canSuspend));
0145     }
0146     {
0147         auto watcher = new QDBusPendingCallWatcher(m_login1->CanHybridSleep(), this);
0148         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canHybridSuspend));
0149     }
0150     {
0151         auto watcher = new QDBusPendingCallWatcher(m_login1->CanHibernate(), this);
0152         connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canHibernate));
0153     }
0154 
0155     connect(m_login1, &OrgFreedesktopLogin1ManagerInterface::PrepareForSleep, this, [this](bool sleeping) {
0156         if (sleeping) {
0157             Q_EMIT aboutToSuspend();
0158         } else {
0159             Q_EMIT resumingFromSuspend();
0160         }
0161     });
0162 }
0163 
0164 SessionManagement::State LogindSessionBackend::state() const
0165 {
0166     return m_state;
0167 }
0168 
0169 void LogindSessionBackend::shutdown()
0170 {
0171     // logind will confirm credentials with the caller, if the app quits after sending this
0172     // this may fail
0173     m_login1->PowerOff(true).waitForFinished();
0174 }
0175 
0176 void LogindSessionBackend::reboot()
0177 {
0178     m_login1->Reboot(true).waitForFinished();
0179 }
0180 
0181 void LogindSessionBackend::suspend()
0182 {
0183     // these need to be synchronous as well - ksmserver-logout-greeter specifically calls these
0184     // and will quit immediately after
0185     m_login1->Suspend(true).waitForFinished();
0186 }
0187 
0188 void LogindSessionBackend::hybridSuspend()
0189 {
0190     m_login1->HybridSleep(true).waitForFinished();
0191 }
0192 
0193 void LogindSessionBackend::hibernate()
0194 {
0195     m_login1->Hibernate(true).waitForFinished();
0196 }
0197 
0198 bool LogindSessionBackend::canShutdown() const
0199 {
0200     return m_canShutdown;
0201 }
0202 
0203 bool LogindSessionBackend::canReboot() const
0204 {
0205     return m_canReboot;
0206 }
0207 
0208 bool LogindSessionBackend::canSuspend() const
0209 {
0210     return m_canSuspend;
0211 }
0212 
0213 bool LogindSessionBackend::canHybridSuspend() const
0214 {
0215     return m_canHybridSuspend;
0216 }
0217 
0218 bool LogindSessionBackend::canHibernate() const
0219 {
0220     return m_canHibernate;
0221 }
0222 
0223 /*********************************************************************************/
0224 
0225 bool ConsoleKitSessionBackend::exists()
0226 {
0227     return QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.login1"));
0228 }
0229 
0230 ConsoleKitSessionBackend::ConsoleKitSessionBackend()
0231 {
0232     m_ck = new OrgFreedesktopConsoleKitManagerInterface(QStringLiteral("org.freedesktop.ConsoleKit"),
0233                                                         QStringLiteral("/org/freedesktop/ConsoleKit/Manager"),
0234                                                         QDBusConnection::systemBus(),
0235                                                         this);
0236     m_upower = new OrgFreedesktopUPowerInterface(QStringLiteral("org.freedesktop.UPower"),
0237                                                  QStringLiteral("/org/freedesktop/UPower"),
0238                                                  QDBusConnection::systemBus(),
0239                                                  this);
0240 
0241     auto canStop = m_ck->CanStop();
0242     canStop.waitForFinished();
0243     m_canShutdown = canStop.value();
0244 
0245     auto canRestart = m_ck->CanRestart();
0246     canRestart.waitForFinished();
0247     m_canReboot = canRestart.value();
0248 
0249     m_canSuspend = m_upower->canSuspend();
0250     m_canHibernate = m_upower->canHibernate();
0251 
0252     connect(m_upower, &OrgFreedesktopUPowerInterface::NotifySleep, this, &SessionBackend::aboutToSuspend);
0253     connect(m_upower, &OrgFreedesktopUPowerInterface::Resuming, this, &SessionBackend::resumingFromSuspend);
0254 
0255     m_state = SessionManagement::State::Ready;
0256 }
0257 
0258 SessionManagement::State ConsoleKitSessionBackend::state() const
0259 {
0260     return m_state;
0261 }
0262 
0263 void ConsoleKitSessionBackend::shutdown()
0264 {
0265     m_ck->Stop();
0266 }
0267 
0268 void ConsoleKitSessionBackend::reboot()
0269 {
0270     m_ck->Restart();
0271 }
0272 
0273 void ConsoleKitSessionBackend::suspend()
0274 {
0275     m_upower->Suspend();
0276 }
0277 
0278 void ConsoleKitSessionBackend::hibernate()
0279 {
0280     m_upower->Hibernate();
0281 }
0282 
0283 bool ConsoleKitSessionBackend::canShutdown() const
0284 {
0285     return m_canShutdown;
0286 }
0287 
0288 bool ConsoleKitSessionBackend::canReboot() const
0289 {
0290     return m_canReboot;
0291 }
0292 
0293 bool ConsoleKitSessionBackend::canSuspend() const
0294 {
0295     return m_canSuspend;
0296 }
0297 
0298 bool ConsoleKitSessionBackend::canHibernate() const
0299 {
0300     return m_canHibernate;
0301 }