Warning, file /plasma/plasma-workspace/ksmserver/logout.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     ksmserver - the KDE session management server
0003 
0004     SPDX-FileCopyrightText: 2000 Matthias Ettrich <ettrich@kde.org>
0005 
0006     SPDX-FileContributor: Oswald Buddenhagen <ob6@inf.tu-dresden.de>
0007 
0008     some code taken from the dcopserver (part of the KDE libraries), which is
0009     SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
0010     SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
0011 
0012     SPDX-License-Identifier: MIT
0013 */
0014 
0015 #include <config-ksmserver.h>
0016 #include <config-unix.h> // HAVE_LIMITS_H
0017 #include <config-workspace.h>
0018 
0019 #include <ksmserver_debug.h>
0020 
0021 #include <pwd.h>
0022 #include <sys/param.h>
0023 #include <sys/stat.h>
0024 #include <sys/types.h>
0025 #ifdef HAVE_SYS_TIME_H
0026 #include <sys/time.h>
0027 #endif
0028 #include <sys/socket.h>
0029 #include <sys/un.h>
0030 
0031 #include <assert.h>
0032 #include <errno.h>
0033 #include <signal.h>
0034 #include <stdlib.h>
0035 #include <string.h>
0036 #include <time.h>
0037 #include <unistd.h>
0038 
0039 #ifdef HAVE_LIMITS_H
0040 #include <limits.h>
0041 #endif
0042 
0043 #include <QApplication>
0044 #include <QFile>
0045 #include <QFutureWatcher>
0046 #include <QTimer>
0047 #include <QtConcurrentRun>
0048 
0049 #include "client.h"
0050 #include "global.h"
0051 #include "server.h"
0052 #include <KConfig>
0053 #include <KConfigGroup>
0054 #include <KLocalizedString>
0055 #include <KNotification>
0056 #include <KSharedConfig>
0057 #include <kdisplaymanager.h>
0058 
0059 #include "kwinsession_interface.h"
0060 #include "logoutprompt_interface.h"
0061 #include "shutdown_interface.h"
0062 
0063 enum KWinSessionState {
0064     Normal = 0,
0065     Saving = 1,
0066     Quitting = 2,
0067 };
0068 
0069 void KSMServer::logout(int confirm, int sdtype, int sdmode)
0070 {
0071     // KDE5: remove me
0072     if (sdtype == KWorkSpace::ShutdownTypeLogout)
0073         sdtype = KWorkSpace::ShutdownTypeNone;
0074 
0075     shutdown((KWorkSpace::ShutdownConfirm)confirm, (KWorkSpace::ShutdownType)sdtype, (KWorkSpace::ShutdownMode)sdmode);
0076 }
0077 
0078 bool KSMServer::closeSession()
0079 {
0080     qCDebug(KSMSERVER) << "Close session called. Current state is:" << state;
0081 
0082     Q_ASSERT(calledFromDBus());
0083     setDelayedReply(true);
0084 
0085     const QDBusMessage callerContext = message();
0086 
0087     auto conn = QSharedPointer<QMetaObject::Connection>::create(QMetaObject::Connection());
0088     *conn = connect(this, &KSMServer::logoutFinished, this, [callerContext, conn](bool sessionClosed) {
0089         auto reply = callerContext.createReply(sessionClosed);
0090         QDBusConnection::sessionBus().send(reply);
0091         QObject::disconnect(*conn);
0092     });
0093 
0094     performLogout();
0095     return false;
0096 }
0097 
0098 bool KSMServer::canShutdown()
0099 {
0100     return KDisplayManager().canShutdown();
0101 }
0102 
0103 bool KSMServer::isShuttingDown() const
0104 {
0105     return state >= Shutdown;
0106 }
0107 
0108 // this method exists purely for compatibility
0109 void KSMServer::shutdown(KWorkSpace::ShutdownConfirm confirm, KWorkSpace::ShutdownType sdtype, KWorkSpace::ShutdownMode sdmode)
0110 {
0111     qCDebug(KSMSERVER) << "Shutdown called with confirm " << confirm << " type " << sdtype << " and mode " << sdmode;
0112     if (state >= Shutdown) // already performing shutdown
0113         return;
0114     if (state != Idle) // performing startup
0115     {
0116         return;
0117     }
0118 
0119     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0120     config->reparseConfiguration(); // config may have changed in the KControl module
0121 
0122     KConfigGroup cg(config, "General");
0123 
0124     bool logoutConfirmed = (confirm == KWorkSpace::ShutdownConfirmYes      ? false
0125                                 : confirm == KWorkSpace::ShutdownConfirmNo ? true
0126                                                                            : !cg.readEntry("confirmLogout", true));
0127 
0128     int shutdownType = (sdtype != KWorkSpace::ShutdownTypeDefault ? sdtype : cg.readEntry("shutdownType", (int)KWorkSpace::ShutdownType::ShutdownTypeLogout));
0129 
0130     if (!logoutConfirmed) {
0131         OrgKdeLogoutPromptInterface logoutPrompt(QStringLiteral("org.kde.LogoutPrompt"), QStringLiteral("/LogoutPrompt"), QDBusConnection::sessionBus());
0132         switch (shutdownType) {
0133         case KWorkSpace::ShutdownTypeHalt:
0134             logoutPrompt.promptShutDown();
0135             break;
0136         case KWorkSpace::ShutdownTypeReboot:
0137             logoutPrompt.promptReboot();
0138             break;
0139         case KWorkSpace::ShutdownTypeNone:
0140             Q_FALLTHROUGH();
0141         default:
0142             logoutPrompt.promptLogout();
0143             break;
0144         }
0145     } else {
0146         OrgKdeShutdownInterface shutdownIface(QStringLiteral("org.kde.Shutdown"), QStringLiteral("/Shutdown"), QDBusConnection::sessionBus());
0147         switch (shutdownType) {
0148         case KWorkSpace::ShutdownTypeHalt:
0149             shutdownIface.logoutAndShutdown();
0150             break;
0151         case KWorkSpace::ShutdownTypeReboot:
0152             shutdownIface.logoutAndReboot();
0153             break;
0154         case KWorkSpace::ShutdownTypeNone:
0155             Q_FALLTHROUGH();
0156         default:
0157             shutdownIface.logout();
0158             break;
0159         }
0160     }
0161 }
0162 
0163 void KSMServer::performLogout()
0164 {
0165     if (state >= Shutdown) { // already performing shutdown
0166         return;
0167     }
0168     if (state != Idle) {
0169         QTimer::singleShot(1000, this, &KSMServer::performLogout);
0170     }
0171 
0172     auto setStateReply = m_kwinInterface->setState(KWinSessionState::Saving);
0173 
0174     state = Shutdown;
0175 
0176     // shall we save the session on logout?
0177     KConfigGroup cg(KSharedConfig::openConfig(), "General");
0178     saveSession = (cg.readEntry("loginMode", QStringLiteral("restorePreviousLogout")) == QLatin1String("restorePreviousLogout"));
0179 
0180     qCDebug(KSMSERVER) << "saveSession is " << saveSession;
0181 
0182     if (saveSession)
0183         sessionGroup = QStringLiteral("Session: ") + QString::fromLocal8Bit(SESSION_PREVIOUS_LOGOUT);
0184 
0185     saveType = saveSession ? SmSaveBoth : SmSaveGlobal;
0186 #ifndef NO_LEGACY_SESSION_MANAGEMENT
0187     performLegacySessionSave();
0188 #endif
0189     startProtection();
0190 
0191     // Tell KWin to start saving before we start tearing down clients
0192     // as any "Save changes?" prompt might meddle with the state
0193     if (saveSession) {
0194         setStateReply.waitForFinished(); // do we have to wait for this to finish?
0195 
0196         qCDebug(KSMSERVER) << "Telling KWin we're about to save session" << currentSession();
0197 
0198         auto saveSessionCall = m_kwinInterface->aboutToSaveSession(currentSession());
0199         // We need to wait for KWin to save the initial state, e.g. active client and
0200         // current desktop before we signal any clients to quit. They might bring up
0201         // "Save changes?" prompts altering the state.
0202         // KWin doesn't talk to KSMServer directly anymore, so this won't deadlock.
0203         saveSessionCall.waitForFinished();
0204     }
0205 
0206     const auto pendingClients = clients;
0207 
0208     for (KSMClient *c : pendingClients) {
0209         c->resetState();
0210 
0211         SmsSaveYourself(c->connection(), saveType, true, SmInteractStyleAny, false);
0212     }
0213 
0214     qCDebug(KSMSERVER) << "clients should be empty, " << clients.count();
0215 
0216     if (clients.isEmpty())
0217         completeShutdownOrCheckpoint();
0218 }
0219 
0220 void KSMServer::saveCurrentSession()
0221 {
0222     if (state != Idle)
0223         return;
0224 
0225     if (currentSession().isEmpty() || currentSession() == QString::fromLocal8Bit(SESSION_PREVIOUS_LOGOUT))
0226         sessionGroup = QLatin1String("Session: ") + QString::fromLocal8Bit(SESSION_BY_USER);
0227 
0228     state = Checkpoint;
0229 
0230     saveType = SmSaveLocal;
0231     saveSession = true;
0232 #ifndef NO_LEGACY_SESSION_MANAGEMENT
0233     performLegacySessionSave();
0234 #endif
0235 
0236     auto aboutToSaveCall = m_kwinInterface->aboutToSaveSession(currentSession());
0237     aboutToSaveCall.waitForFinished();
0238 
0239     const auto pendingClients = clients;
0240     for (KSMClient *c : pendingClients) {
0241         c->resetState();
0242         SmsSaveYourself(c->connection(), saveType, false, SmInteractStyleNone, false);
0243     }
0244     if (clients.isEmpty())
0245         completeShutdownOrCheckpoint();
0246 }
0247 
0248 void KSMServer::saveCurrentSessionAs(const QString &session)
0249 {
0250     if (state != Idle)
0251         return;
0252     sessionGroup = QStringLiteral("Session: ") + session;
0253     saveCurrentSession();
0254 }
0255 
0256 // callbacks
0257 void KSMServer::saveYourselfDone(KSMClient *client, bool success)
0258 {
0259     if (state == Idle) {
0260         // State saving when it's not shutdown or checkpoint. Probably
0261         // a shutdown was canceled and the client is finished saving
0262         // only now. Discard the saved state in order to avoid
0263         // the saved data building up.
0264         QStringList discard = client->discardCommand();
0265         if (!discard.isEmpty())
0266             executeCommand(discard);
0267         return;
0268     }
0269     if (success) {
0270         client->saveYourselfDone = true;
0271         completeShutdownOrCheckpoint();
0272     } else {
0273         // fake success to make KDE's logout not block with broken
0274         // apps. A perfect ksmserver would display a warning box at
0275         // the very end.
0276         client->saveYourselfDone = true;
0277         completeShutdownOrCheckpoint();
0278     }
0279     startProtection();
0280 }
0281 
0282 void KSMServer::interactRequest(KSMClient *client, int /*dialogType*/)
0283 {
0284     if (state == Shutdown || state == ClosingSubSession)
0285         client->pendingInteraction = true;
0286     else
0287         SmsInteract(client->connection());
0288 
0289     handlePendingInteractions();
0290 }
0291 
0292 void KSMServer::interactDone(KSMClient *client, bool cancelShutdown_)
0293 {
0294     if (client != clientInteracting)
0295         return; // should not happen
0296     clientInteracting = nullptr;
0297     if (cancelShutdown_)
0298         cancelShutdown(client);
0299     else
0300         handlePendingInteractions();
0301 }
0302 
0303 void KSMServer::phase2Request(KSMClient *client)
0304 {
0305     client->waitForPhase2 = true;
0306     client->wasPhase2 = true;
0307     completeShutdownOrCheckpoint();
0308 }
0309 
0310 void KSMServer::handlePendingInteractions()
0311 {
0312     if (clientInteracting)
0313         return;
0314 
0315     foreach (KSMClient *c, clients) {
0316         if (c->pendingInteraction) {
0317             clientInteracting = c;
0318             c->pendingInteraction = false;
0319             break;
0320         }
0321     }
0322     if (clientInteracting) {
0323         endProtection();
0324         SmsInteract(clientInteracting->connection());
0325     } else {
0326         startProtection();
0327     }
0328 }
0329 
0330 void KSMServer::cancelShutdown(KSMClient *c)
0331 {
0332     clientInteracting = nullptr;
0333     qCDebug(KSMSERVER) << state;
0334     if (state == ClosingSubSession) {
0335         clientsToKill.clear();
0336         clientsToSave.clear();
0337         Q_EMIT subSessionCloseCanceled();
0338     } else {
0339         qCDebug(KSMSERVER) << "Client " << c->program() << " (" << c->clientId() << ") canceled shutdown.";
0340         KNotification::event(QStringLiteral("cancellogout"), i18n("Logout canceled by '%1'", c->program()), QPixmap(), nullptr, KNotification::DefaultEvent);
0341         foreach (KSMClient *c, clients) {
0342             SmsShutdownCancelled(c->connection());
0343             if (c->saveYourselfDone) {
0344                 // Discard also saved state.
0345                 QStringList discard = c->discardCommand();
0346                 if (!discard.isEmpty())
0347                     executeCommand(discard);
0348             }
0349             c->resetState();
0350         }
0351     }
0352     state = Idle;
0353 
0354     m_kwinInterface->setState(KWinSessionState::Normal);
0355 
0356     Q_EMIT logoutFinished(false);
0357 }
0358 
0359 void KSMServer::startProtection()
0360 {
0361     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0362     config->reparseConfiguration(); // config may have changed in the KControl module
0363     KConfigGroup cg(config, "General");
0364 
0365     int timeout = cg.readEntry("clientShutdownTimeoutSecs", 15) * 1000;
0366 
0367     protectionTimer.setSingleShot(true);
0368     protectionTimer.start(timeout);
0369 }
0370 
0371 void KSMServer::endProtection()
0372 {
0373     protectionTimer.stop();
0374 }
0375 
0376 /*
0377 Internal protection slot, invoked when clients do not react during
0378 shutdown.
0379 */
0380 void KSMServer::protectionTimeout()
0381 {
0382     if ((state != Shutdown && state != Checkpoint && state != ClosingSubSession) || clientInteracting)
0383         return;
0384 
0385     foreach (KSMClient *c, clients) {
0386         if (!c->saveYourselfDone && !c->waitForPhase2) {
0387             qCDebug(KSMSERVER) << "protectionTimeout: client " << c->program() << "(" << c->clientId() << ")";
0388             c->saveYourselfDone = true;
0389         }
0390     }
0391     completeShutdownOrCheckpoint();
0392     startProtection();
0393 }
0394 
0395 void KSMServer::completeShutdownOrCheckpoint()
0396 {
0397     qCDebug(KSMSERVER) << "completeShutdownOrCheckpoint called";
0398     if (state != Shutdown && state != Checkpoint && state != ClosingSubSession)
0399         return;
0400 
0401     QList<KSMClient *> pendingClients;
0402     if (state == ClosingSubSession)
0403         pendingClients = clientsToSave;
0404     else
0405         pendingClients = clients;
0406 
0407     foreach (KSMClient *c, pendingClients) {
0408         if (!c->saveYourselfDone && !c->waitForPhase2)
0409             return; // not done yet
0410     }
0411 
0412     // do phase 2
0413     bool waitForPhase2 = false;
0414     foreach (KSMClient *c, pendingClients) {
0415         if (!c->saveYourselfDone && c->waitForPhase2) {
0416             c->waitForPhase2 = false;
0417             SmsSaveYourselfPhase2(c->connection());
0418             waitForPhase2 = true;
0419         }
0420     }
0421     if (waitForPhase2)
0422         return;
0423 
0424     if (saveSession)
0425         storeSession();
0426     else
0427         discardSession();
0428 
0429     qCDebug(KSMSERVER) << "state is " << state;
0430     if (state == Shutdown) {
0431         KNotification *n = KNotification::event(QStringLiteral("exitkde"), QString(), QPixmap(), nullptr, KNotification::DefaultEvent); // Plasma says good bye
0432         connect(n, &KNotification::closed, this, &KSMServer::startKilling);
0433         state = WaitingForKNotify;
0434         // https://bugs.kde.org/show_bug.cgi?id=228005
0435         // if sound is not working for some reason (e.g. no phonon
0436         // backends are installed) the closed() signal never happens
0437         // and logoutSoundFinished() never gets called. Add this timer to make
0438         // sure the shutdown procedure continues even if sound system is broken.
0439         QTimer::singleShot(5000, this, [=] {
0440             if (state == WaitingForKNotify) {
0441                 n->deleteLater();
0442                 startKilling();
0443             }
0444         });
0445     } else if (state == Checkpoint) {
0446         foreach (KSMClient *c, clients) {
0447             SmsSaveComplete(c->connection());
0448         }
0449         state = Idle;
0450     } else { // ClosingSubSession
0451         startKillingSubSession();
0452     }
0453 }
0454 
0455 void KSMServer::startKilling()
0456 {
0457     qCDebug(KSMSERVER) << "Starting killing clients";
0458     if (state == Killing) {
0459         // we are already killing
0460         return;
0461     }
0462     // kill all clients
0463     state = Killing;
0464 
0465     m_kwinInterface->setState(KWinSessionState::Quitting);
0466 
0467     foreach (KSMClient *c, clients) {
0468         qCDebug(KSMSERVER) << "startKilling: client " << c->program() << "(" << c->clientId() << ")";
0469         SmsDie(c->connection());
0470     }
0471 
0472     qCDebug(KSMSERVER) << " We killed all clients. We have now clients.count()=" << clients.count() << Qt::endl;
0473     completeKilling();
0474     QTimer::singleShot(10000, this, &KSMServer::timeoutQuit);
0475 }
0476 
0477 void KSMServer::completeKilling()
0478 {
0479     qCDebug(KSMSERVER) << "KSMServer::completeKilling clients.count()=" << clients.count() << Qt::endl;
0480     if (state == Killing) {
0481         if (!clients.isEmpty()) // still waiting for clients to go away
0482             return;
0483         killingCompleted();
0484     }
0485 }
0486 
0487 // shutdown is fully complete
0488 void KSMServer::killingCompleted()
0489 {
0490     Q_EMIT logoutFinished(true);
0491     qApp->quit();
0492 }
0493 
0494 void KSMServer::timeoutQuit()
0495 {
0496     foreach (KSMClient *c, clients) {
0497         qCWarning(KSMSERVER) << "SmsDie timeout, client " << c->program() << "(" << c->clientId() << ")";
0498     }
0499     killingCompleted();
0500 }
0501 
0502 void KSMServer::saveSubSession(const QString &name, QStringList saveAndClose, QStringList saveOnly)
0503 {
0504     if (state != Idle) { // performing startup
0505         qCDebug(KSMSERVER) << "not idle!" << state;
0506         return;
0507     }
0508     qCDebug(KSMSERVER) << name << saveAndClose << saveOnly;
0509     state = ClosingSubSession;
0510     saveType = SmSaveBoth; // both or local? what oes it mean?
0511     saveSession = true;
0512     sessionGroup = QStringLiteral("SubSession: ") + name;
0513 
0514 #ifndef NO_LEGACY_SESSION_MANAGEMENT
0515     // performLegacySessionSave(); FIXME
0516 #endif
0517 
0518     startProtection();
0519     foreach (KSMClient *c, clients) {
0520         if (saveAndClose.contains(QString::fromLocal8Bit(c->clientId()))) {
0521             c->resetState();
0522             SmsSaveYourself(c->connection(), saveType, true, SmInteractStyleAny, false);
0523             clientsToSave << c;
0524             clientsToKill << c;
0525         } else if (saveOnly.contains(QString::fromLocal8Bit(c->clientId()))) {
0526             c->resetState();
0527             SmsSaveYourself(c->connection(), saveType, true, SmInteractStyleAny, false);
0528             clientsToSave << c;
0529         }
0530     }
0531     completeShutdownOrCheckpoint();
0532 }
0533 
0534 void KSMServer::startKillingSubSession()
0535 {
0536     qCDebug(KSMSERVER) << "Starting killing clients";
0537     // kill all clients
0538     state = KillingSubSession;
0539     foreach (KSMClient *c, clientsToKill) {
0540         qCDebug(KSMSERVER) << "completeShutdown: client " << c->program() << "(" << c->clientId() << ")";
0541         SmsDie(c->connection());
0542     }
0543 
0544     qCDebug(KSMSERVER) << " We killed some clients. We have now clients.count()=" << clients.count() << Qt::endl;
0545     completeKillingSubSession();
0546     QTimer::singleShot(10000, this, &KSMServer::signalSubSessionClosed);
0547 }
0548 
0549 void KSMServer::completeKillingSubSession()
0550 {
0551     qCDebug(KSMSERVER) << "KSMServer::completeKillingSubSession clients.count()=" << clients.count() << Qt::endl;
0552     if (state == KillingSubSession) {
0553         if (!clientsToKill.isEmpty()) {
0554             return; // still waiting for clients to go away
0555         }
0556         signalSubSessionClosed();
0557     }
0558 }
0559 
0560 void KSMServer::signalSubSessionClosed()
0561 {
0562     if (state != KillingSubSession)
0563         return;
0564     clientsToKill.clear();
0565     clientsToSave.clear();
0566     // TODO tell the subSession manager the close request was carried out
0567     // so that plasma can close its stuff
0568     state = Idle;
0569     qCDebug(KSMSERVER) << state;
0570     Q_EMIT subSessionClosed();
0571 }