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