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 "sessionmanagement.h"
0008 
0009 #include "sessionmanagementbackend.h"
0010 
0011 #include <KAuthorized>
0012 #include <KConfigGroup>
0013 #include <KSharedConfig>
0014 
0015 #include <iostream>
0016 
0017 #include "ksmserver_interface.h"
0018 #include "logoutprompt_interface.h"
0019 #include "screenlocker_interface.h"
0020 #include "shutdown_interface.h"
0021 
0022 #include "libkworkspace_debug.h"
0023 
0024 using namespace Qt::StringLiterals;
0025 
0026 // add a constructor with the service names and paths pre-populated
0027 class LogoutPromptIface : public OrgKdeLogoutPromptInterface
0028 {
0029     Q_OBJECT
0030 public:
0031     LogoutPromptIface()
0032         : OrgKdeLogoutPromptInterface(QStringLiteral("org.kde.LogoutPrompt"), QStringLiteral("/LogoutPrompt"), QDBusConnection::sessionBus())
0033     {
0034     }
0035 };
0036 
0037 class ShutdownIface : public OrgKdeShutdownInterface
0038 {
0039     Q_OBJECT
0040 public:
0041     ShutdownIface()
0042         : OrgKdeShutdownInterface(QStringLiteral("org.kde.Shutdown"), QStringLiteral("/Shutdown"), QDBusConnection::sessionBus())
0043     {
0044     }
0045 };
0046 
0047 SessionManagement::SessionManagement(QObject *parent)
0048     : QObject(parent)
0049 {
0050     auto backend = SessionBackend::self();
0051     connect(backend, &SessionBackend::stateChanged, this, &SessionManagement::stateChanged);
0052     connect(backend, &SessionBackend::canShutdownChanged, this, &SessionManagement::canShutdownChanged);
0053     connect(backend, &SessionBackend::canRebootChanged, this, &SessionManagement::canRebootChanged);
0054     connect(backend, &SessionBackend::canSuspendChanged, this, &SessionManagement::canSuspendChanged);
0055     connect(backend, &SessionBackend::canHybridSuspendChanged, this, &SessionManagement::canHybridSuspendChanged);
0056     connect(backend, &SessionBackend::canHibernateChanged, this, &SessionManagement::canHibernateChanged);
0057     connect(backend, &SessionBackend::canSuspendThenHibernateChanged, this, &SessionManagement::canSuspendThenHibernateChanged);
0058     connect(backend, &SessionBackend::aboutToSuspend, this, &SessionManagement::aboutToSuspend);
0059     connect(backend, &SessionBackend::resumingFromSuspend, this, &SessionManagement::resumingFromSuspend);
0060 }
0061 
0062 bool SessionManagement::canShutdown() const
0063 {
0064     return canLogout() && SessionBackend::self()->canShutdown();
0065 }
0066 
0067 bool SessionManagement::canReboot() const
0068 {
0069     return canLogout() && SessionBackend::self()->canReboot();
0070 }
0071 
0072 bool SessionManagement::canLogout() const
0073 {
0074     // checking both is for compatibility with old kiosk configs
0075     // authorizeAction is the "correct" one
0076     return KAuthorized::authorizeAction(QStringLiteral("logout")) && KAuthorized::authorize(QStringLiteral("logout"));
0077 }
0078 
0079 bool SessionManagement::canSuspend() const
0080 {
0081     return SessionBackend::self()->canSuspend();
0082 }
0083 
0084 bool SessionManagement::canHybridSuspend() const
0085 {
0086     return SessionBackend::self()->canHybridSuspend();
0087 }
0088 
0089 bool SessionManagement::canHibernate() const
0090 {
0091     return SessionBackend::self()->canHibernate();
0092 }
0093 
0094 bool SessionManagement::canSuspendThenHibernate() const
0095 {
0096     return SessionBackend::self()->canSuspendThenHibernate();
0097 }
0098 
0099 bool SessionManagement::canSwitchUser() const
0100 {
0101     return KAuthorized::authorizeAction(QStringLiteral("start_new_session")) && SessionBackend::self()->canSwitchUser();
0102 }
0103 
0104 bool SessionManagement::canLock() const
0105 {
0106     return KAuthorized::authorizeAction(QStringLiteral("lock_screen"));
0107 }
0108 
0109 bool SessionManagement::canSaveSession() const
0110 {
0111     const KConfigGroup c(KSharedConfig::openConfig(u"ksmserverrc"_s), u"General"_s);
0112     return canLogout() && c.readEntry("loginMode") == QLatin1String("restoreSavedSession");
0113 }
0114 
0115 SessionManagement::State SessionManagement::state() const
0116 {
0117     return SessionBackend::self()->state();
0118 }
0119 
0120 void SessionManagement::requestLogoutPrompt()
0121 {
0122     if (qEnvironmentVariableIntValue("PLASMA_SESSION_GUI_TEST")) {
0123         std::cout << "show logout prompt " << std::endl;
0124         return;
0125     }
0126 
0127     // Don't bother to check for whether the user normally wants confirmation or
0128     // not; if this function was invoked, it means they do want to see the logout
0129     // prompt right now
0130     LogoutPromptIface iface;
0131     iface.promptAll().waitForFinished();
0132 }
0133 
0134 void SessionManagement::requestShutdown(ConfirmationMode confirmationMode)
0135 {
0136     if (!canShutdown()) {
0137         return;
0138     }
0139 
0140     if (qEnvironmentVariableIntValue("PLASMA_SESSION_GUI_TEST")) {
0141         std::cout << "shutdown" << std::endl;
0142         return;
0143     }
0144 
0145     bool confirm = confirmationMode == ConfirmationMode::ForcePrompt;
0146     if (confirmationMode == ConfirmationMode::Default) {
0147         confirm = SessionBackend::self()->confirmLogout();
0148     }
0149     if (confirm) {
0150         LogoutPromptIface iface;
0151         iface.promptShutDown().waitForFinished();
0152     } else {
0153         ShutdownIface iface;
0154         iface.logoutAndShutdown().waitForFinished();
0155     }
0156 }
0157 
0158 void SessionManagement::requestReboot(ConfirmationMode confirmationMode)
0159 {
0160     if (!canReboot()) {
0161         return;
0162     }
0163 
0164     if (qEnvironmentVariableIntValue("PLASMA_SESSION_GUI_TEST")) {
0165         std::cout << "reboot" << std::endl;
0166         return;
0167     }
0168 
0169     bool confirm = confirmationMode == ConfirmationMode::ForcePrompt;
0170     if (confirmationMode == ConfirmationMode::Default) {
0171         confirm = SessionBackend::self()->confirmLogout();
0172     }
0173     if (confirm) {
0174         LogoutPromptIface iface;
0175         iface.promptReboot().waitForFinished();
0176     } else {
0177         ShutdownIface iface;
0178         iface.logoutAndReboot().waitForFinished();
0179     }
0180 }
0181 
0182 void SessionManagement::requestLogout(ConfirmationMode confirmationMode)
0183 {
0184     if (!canLogout()) {
0185         return;
0186     }
0187     bool confirm = confirmationMode == ConfirmationMode::ForcePrompt;
0188     if (confirmationMode == ConfirmationMode::Default) {
0189         confirm = SessionBackend::self()->confirmLogout();
0190     }
0191     if (confirm) {
0192         LogoutPromptIface iface;
0193         iface.promptLogout().waitForFinished();
0194     } else {
0195         ShutdownIface iface;
0196         iface.logout().waitForFinished();
0197     }
0198 }
0199 
0200 void SessionManagement::suspend()
0201 {
0202     if (!canSuspend()) {
0203         return;
0204     }
0205     SessionBackend::self()->suspend();
0206 }
0207 
0208 void SessionManagement::hybridSuspend()
0209 {
0210     if (!canHybridSuspend()) {
0211         return;
0212     }
0213     SessionBackend::self()->hybridSuspend();
0214 }
0215 
0216 void SessionManagement::hibernate()
0217 {
0218     if (!canHibernate()) {
0219         return;
0220     }
0221     SessionBackend::self()->hibernate();
0222 }
0223 
0224 void SessionManagement::suspendThenHibernate()
0225 {
0226     if (!canSuspendThenHibernate()) {
0227         return;
0228     }
0229     SessionBackend::self()->suspendThenHibernate();
0230 }
0231 
0232 void SessionManagement::lock()
0233 {
0234     if (!canLock()) {
0235         return;
0236     }
0237     OrgFreedesktopScreenSaverInterface iface(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus());
0238     iface.Lock();
0239 }
0240 
0241 void SessionManagement::switchUser()
0242 {
0243     if (!canSwitchUser() || !canLock()) {
0244         return;
0245     }
0246 
0247     if (!qEnvironmentVariableIsSet("XDG_SEAT_PATH")) {
0248         qCWarning(LIBKWORKSPACE_DEBUG) << "Cannot switch user: XDG_SEAT_PATH not set";
0249         return;
0250     }
0251 
0252     // lock first
0253     OrgFreedesktopScreenSaverInterface screenSaverIface(QStringLiteral("org.freedesktop.ScreenSaver"),
0254                                                         QStringLiteral("/ScreenSaver"),
0255                                                         QDBusConnection::sessionBus());
0256     QDBusPendingReply<> pendingLock = screenSaverIface.Lock();
0257 
0258     // then tell the display manager to switch
0259     auto watcher = new QDBusPendingCallWatcher(pendingLock, this);
0260     connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater);
0261     connect(watcher, &QDBusPendingCallWatcher::finished, this, [](QDBusPendingCallWatcher *watcher) {
0262         if (watcher->isError()) {
0263             qCWarning(LIBKWORKSPACE_DEBUG) << "Failed to lock screen before switching user:" << watcher->error().message();
0264             return;
0265         }
0266         QDBusMessage switchToGreeterMessage = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DisplayManager"),
0267                                                                              qgetenv("XDG_SEAT_PATH"),
0268                                                                              QStringLiteral("org.freedesktop.DisplayManager.Seat"),
0269                                                                              "SwitchToGreeter");
0270 
0271         QDBusConnection::systemBus().asyncCall(switchToGreeterMessage);
0272     });
0273 }
0274 
0275 void SessionManagement::saveSession()
0276 {
0277     if (!canSaveSession()) {
0278         return;
0279     }
0280     OrgKdeKSMServerInterfaceInterface ksmserver(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus());
0281     ksmserver.saveCurrentSession();
0282 }
0283 
0284 #include "sessionmanagement.moc"