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 }