File indexing completed on 2024-05-19 05:39:09

0001 #include "shutdown.h"
0002 #include "shutdownadaptor.h"
0003 
0004 #include <QCoreApplication>
0005 #include <QDBusConnection>
0006 #include <QDir>
0007 #include <QProcess>
0008 #include <QStandardPaths>
0009 
0010 #include "debug.h"
0011 #include "ksmserver_interface.h"
0012 #include "kwin_interface.h"
0013 #include "sessionmanagementbackend.h"
0014 
0015 Shutdown::Shutdown(QObject *parent)
0016     : QObject(parent)
0017 {
0018     new ShutdownAdaptor(this);
0019     QDBusConnection::sessionBus().registerObject(QStringLiteral("/Shutdown"), QStringLiteral("org.kde.Shutdown"), this);
0020     QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.Shutdown"));
0021 }
0022 
0023 void Shutdown::logout()
0024 {
0025     startLogout(KWorkSpace::ShutdownTypeNone);
0026 }
0027 
0028 void Shutdown::logoutAndShutdown()
0029 {
0030     startLogout(KWorkSpace::ShutdownTypeHalt);
0031 }
0032 
0033 void Shutdown::logoutAndReboot()
0034 {
0035     startLogout(KWorkSpace::ShutdownTypeReboot);
0036 }
0037 
0038 void Shutdown::startLogout(KWorkSpace::ShutdownType shutdownType)
0039 {
0040     m_shutdownType = shutdownType;
0041 
0042     OrgKdeKSMServerInterfaceInterface ksmserverIface(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus());
0043     ksmserverIface.setTimeout(
0044         INT32_MAX); // KSMServer closeSession can take a long time to reply, as apps may have prompts.  Value corresponds to DBUS_TIMEOUT_INFINITE
0045 
0046     auto closeSessionReply = ksmserverIface.closeSession();
0047     auto watcher = new QDBusPendingCallWatcher(closeSessionReply, this);
0048     connect(watcher, &QDBusPendingCallWatcher::finished, this, [closeSessionReply, watcher, this]() {
0049         watcher->deleteLater();
0050         if (closeSessionReply.isError()) {
0051             qCInfo(PLASMA_SESSION) << "ksmserver failed to complete logout with error" << closeSessionReply.error().name() << " continuining with shutdown";
0052             ksmServerComplete();
0053             return;
0054         }
0055         if (closeSessionReply.value()) {
0056             ksmServerComplete();
0057         } else {
0058             logoutCancelled();
0059         }
0060     });
0061 }
0062 
0063 void Shutdown::ksmServerComplete()
0064 {
0065     OrgKdeKWinSessionInterface kwinInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Session"), QDBusConnection::sessionBus());
0066     kwinInterface.setTimeout(INT32_MAX);
0067     auto reply = kwinInterface.closeWaylandWindows();
0068     auto watcher = new QDBusPendingCallWatcher(reply, this);
0069     connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
0070         watcher->deleteLater();
0071         OrgKdeKSMServerInterfaceInterface ksmserverIface(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus());
0072         auto reply = QDBusReply<bool>(*watcher);
0073         if (!reply.isValid()) {
0074             qCWarning(PLASMA_SESSION) << "KWin failed to complete logout";
0075             ksmserverIface.resetLogout();
0076             logoutCancelled();
0077             return;
0078         }
0079         if (reply.value()) {
0080             logoutComplete();
0081         } else {
0082             ksmserverIface.resetLogout();
0083             logoutCancelled();
0084         }
0085     });
0086 }
0087 
0088 void Shutdown::logoutCancelled()
0089 {
0090     m_shutdownType = KWorkSpace::ShutdownTypeNone;
0091     qApp->quit();
0092 }
0093 
0094 void Shutdown::logoutComplete()
0095 {
0096     runShutdownScripts();
0097 
0098     auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
0099                                               QStringLiteral("/org/freedesktop/systemd1"),
0100                                               QStringLiteral("org.freedesktop.systemd1.Manager"),
0101                                               QStringLiteral("StopUnit"));
0102     msg << QStringLiteral("graphical-session.target") << QStringLiteral("fail");
0103     QDBusReply<QDBusObjectPath> reply = QDBusConnection::sessionBus().call(msg);
0104 
0105     if (!reply.isValid()) {
0106         auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.ksmserver"),
0107                                                   QStringLiteral("/MainApplication"),
0108                                                   QStringLiteral("org.qtproject.Qt.QCoreApplication"),
0109                                                   QStringLiteral("quit"));
0110         QDBusConnection::sessionBus().call(msg);
0111 
0112         OrgKdeKWinSessionInterface kwinInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Session"), QDBusConnection::sessionBus());
0113         QDBusPendingReply<> reply = kwinInterface.quit();
0114         reply.waitForFinished();
0115     }
0116 
0117     if (m_shutdownType == KWorkSpace::ShutdownTypeHalt) {
0118         SessionBackend::self()->shutdown();
0119     } else if (m_shutdownType == KWorkSpace::ShutdownTypeReboot) {
0120         SessionBackend::self()->reboot();
0121     } else { // logout
0122         qApp->quit();
0123     }
0124 }
0125 
0126 void Shutdown::runShutdownScripts()
0127 {
0128     const QStringList shutdownFolders =
0129         QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("plasma-workspace/shutdown"), QStandardPaths::LocateDirectory);
0130     for (const QString &shutDownFolder : shutdownFolders) {
0131         QDir dir(shutDownFolder);
0132 
0133         const QStringList entries = dir.entryList(QDir::Files);
0134         for (const QString &file : entries) {
0135             // Don't execute backup files
0136             if (!file.endsWith(QLatin1Char('~')) && !file.endsWith(QLatin1String(".bak")) && (file[0] != QLatin1Char('%') || !file.endsWith(QLatin1Char('%')))
0137                 && (file[0] != QLatin1Char('#') || !file.endsWith(QLatin1Char('#')))) {
0138                 const QString fullPath = dir.absolutePath() + QLatin1Char('/') + file;
0139 
0140                 qCDebug(PLASMA_SESSION) << "running shutdown script" << fullPath;
0141                 QProcess::execute(fullPath, QStringList());
0142             }
0143         }
0144     }
0145 }