File indexing completed on 2024-04-21 16:21:53
0001 /* 0002 ksmserver - the KDE session management server 0003 0004 SPDX-FileCopyrightText: 2000 Matthias Ettrich <ettrich@kde.org> 0005 SPDX-FileCopyrightText: 2005 Lubos Lunak <l.lunak@kde.org> 0006 0007 SPDX-FileContributor: Oswald Buddenhagen <ob6@inf.tu-dresden.de> 0008 0009 some code taken from the dcopserver (part of the KDE libraries), which is 0010 SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org> 0011 SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org> 0012 0013 SPDX-License-Identifier: MIT 0014 */ 0015 0016 #include "server.h" 0017 #include "client.h" 0018 #include "global.h" 0019 #include "kglobalaccel.h" 0020 #include "klocalizedstring.h" 0021 #include "ksmserver_debug.h" 0022 #include "ksmserverinterfaceadaptor.h" 0023 0024 #include <config-ksmserver.h> 0025 #include <config-unix.h> // HAVE_LIMITS_H 0026 #include <config-workspace.h> 0027 #include <pwd.h> 0028 #include <sys/param.h> 0029 #include <sys/stat.h> 0030 #include <sys/types.h> 0031 #ifdef HAVE_SYS_TIME_H 0032 #include <sys/time.h> 0033 #endif 0034 #include <sys/socket.h> 0035 #include <sys/un.h> 0036 0037 #include <assert.h> 0038 #include <errno.h> 0039 #include <fcntl.h> 0040 #include <signal.h> 0041 #include <stdlib.h> 0042 #include <string.h> 0043 #include <time.h> 0044 #include <unistd.h> 0045 0046 #ifdef HAVE_LIMITS_H 0047 #include <limits.h> 0048 #endif 0049 0050 #include <QAction> 0051 #include <QApplication> 0052 #include <QDBusConnection> 0053 #include <QDebug> 0054 #include <QFile> 0055 #include <QPushButton> 0056 #include <QRegularExpression> 0057 #include <QSocketNotifier> 0058 #include <QStandardPaths> 0059 0060 #include <KSharedConfig> 0061 #include <QTemporaryFile> 0062 #include <kactioncollection.h> 0063 #include <kauthorized.h> 0064 #include <kconfig.h> 0065 #include <kconfiggroup.h> 0066 #include <kdesktopfile.h> 0067 #include <kprocess.h> 0068 #include <kshell.h> 0069 0070 #include <KApplicationTrader> 0071 #include <KIO/CommandLauncherJob> 0072 #include <KIO/DesktopExecParser> 0073 #include <KService> 0074 0075 #include <KScreenLocker/KsldApp> 0076 0077 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0078 #include <private/qtx11extras_p.h> 0079 #else 0080 #include <QX11Info> 0081 #endif 0082 #include <krandom.h> 0083 #include <qstandardpaths.h> 0084 #include <startup_interface.h> 0085 0086 #include "kscreenlocker_interface.h" 0087 #include "kwinsession_interface.h" 0088 0089 #include <updatelaunchenvjob.h> 0090 0091 extern "C" { 0092 #include <X11/ICE/ICEmsg.h> 0093 #include <X11/ICE/ICEproto.h> 0094 #include <X11/ICE/ICEutil.h> 0095 } 0096 0097 // ICEmsg.h has a static_assert macro, which conflicts with C++'s static_assert. 0098 #ifdef static_assert 0099 #undef static_assert 0100 #endif 0101 0102 KSMServer *the_server = nullptr; 0103 0104 KSMServer *KSMServer::self() 0105 { 0106 return the_server; 0107 } 0108 0109 /*! Utility function to execute a command on the local machine. Used 0110 * to restart applications. 0111 */ 0112 void KSMServer::startApplication(const QStringList &cmd, const QString &clientMachine, const QString &userId) 0113 { 0114 QStringList command = cmd; 0115 if (command.isEmpty()) 0116 return; 0117 if (!userId.isEmpty()) { 0118 struct passwd *pw = getpwuid(getuid()); 0119 if (pw != nullptr && userId != QString::fromLocal8Bit(pw->pw_name)) { 0120 command.prepend(QStringLiteral("--")); 0121 command.prepend(userId); 0122 command.prepend(QStringLiteral("-u")); 0123 command.prepend(QStandardPaths::findExecutable(QStringLiteral("kdesu"))); 0124 } 0125 } 0126 if (!clientMachine.isEmpty() && clientMachine != QLatin1String("localhost")) { 0127 command.prepend(clientMachine); 0128 command.prepend(xonCommand); // "xon" by default 0129 } 0130 0131 const QString app = command.takeFirst(); 0132 const QStringList argList = command; 0133 auto *job = new KIO::CommandLauncherJob(app, argList); 0134 auto apps = KApplicationTrader::query([&app](const KService::Ptr service) { 0135 const QString binary = KIO::DesktopExecParser::executablePath(service->exec()); 0136 return !service->noDisplay() && !binary.isEmpty() && app.endsWith(binary); 0137 }); 0138 if (!apps.empty()) { 0139 job->setDesktopName(apps[0]->desktopEntryName()); 0140 } 0141 job->start(); 0142 } 0143 0144 /*! Utility function to execute a command on the local machine. Used 0145 * to discard session data 0146 */ 0147 void KSMServer::executeCommand(const QStringList &command) 0148 { 0149 if (command.isEmpty()) 0150 return; 0151 0152 KProcess::execute(command); 0153 } 0154 0155 IceAuthDataEntry *authDataEntries = nullptr; 0156 0157 static QTemporaryFile *remTempFile = nullptr; 0158 0159 static IceListenObj *listenObjs = nullptr; 0160 int numTransports = 0; 0161 static bool only_local = 0; 0162 0163 static Bool HostBasedAuthProc(char * /*hostname*/) 0164 { 0165 if (only_local) 0166 return true; 0167 else 0168 return false; 0169 } 0170 0171 Status KSMRegisterClientProc(SmsConn /* smsConn */, SmPointer managerData, char *previousId) 0172 { 0173 KSMClient *client = (KSMClient *)managerData; 0174 client->registerClient(previousId); 0175 return 1; 0176 } 0177 0178 void KSMInteractRequestProc(SmsConn /* smsConn */, SmPointer managerData, int dialogType) 0179 { 0180 the_server->interactRequest((KSMClient *)managerData, dialogType); 0181 } 0182 0183 void KSMInteractDoneProc(SmsConn /* smsConn */, SmPointer managerData, Bool cancelShutdown) 0184 { 0185 the_server->interactDone((KSMClient *)managerData, cancelShutdown); 0186 } 0187 0188 void KSMSaveYourselfRequestProc(SmsConn smsConn, SmPointer /* managerData */, int saveType, Bool shutdown, int interactStyle, Bool fast, Bool global) 0189 { 0190 if (shutdown) { 0191 the_server->shutdown(fast ? KWorkSpace::ShutdownConfirmNo : KWorkSpace::ShutdownConfirmDefault, 0192 KWorkSpace::ShutdownTypeDefault, 0193 KWorkSpace::ShutdownModeDefault); 0194 } else if (!global) { 0195 SmsSaveYourself(smsConn, saveType, false, interactStyle, fast); 0196 SmsSaveComplete(smsConn); 0197 } 0198 // else checkpoint only, ksmserver does not yet support this 0199 // mode. Will come for KDE 3.1 0200 } 0201 0202 void KSMSaveYourselfPhase2RequestProc(SmsConn /* smsConn */, SmPointer managerData) 0203 { 0204 the_server->phase2Request((KSMClient *)managerData); 0205 } 0206 0207 void KSMSaveYourselfDoneProc(SmsConn /* smsConn */, SmPointer managerData, Bool success) 0208 { 0209 the_server->saveYourselfDone((KSMClient *)managerData, success); 0210 } 0211 0212 void KSMCloseConnectionProc(SmsConn smsConn, SmPointer managerData, int count, char **reasonMsgs) 0213 { 0214 the_server->deleteClient((KSMClient *)managerData); 0215 if (count) 0216 SmFreeReasons(count, reasonMsgs); 0217 IceConn iceConn = SmsGetIceConnection(smsConn); 0218 SmsCleanUp(smsConn); 0219 IceSetShutdownNegotiation(iceConn, false); 0220 IceCloseConnection(iceConn); 0221 } 0222 0223 void KSMSetPropertiesProc(SmsConn /* smsConn */, SmPointer managerData, int numProps, SmProp **props) 0224 { 0225 KSMClient *client = (KSMClient *)managerData; 0226 for (int i = 0; i < numProps; i++) { 0227 SmProp *p = client->property(props[i]->name); 0228 if (p) { 0229 client->properties.removeAll(p); 0230 SmFreeProperty(p); 0231 } 0232 client->properties.append(props[i]); 0233 } 0234 0235 if (numProps) 0236 free(props); 0237 } 0238 0239 void KSMDeletePropertiesProc(SmsConn /* smsConn */, SmPointer managerData, int numProps, char **propNames) 0240 { 0241 KSMClient *client = (KSMClient *)managerData; 0242 for (int i = 0; i < numProps; i++) { 0243 SmProp *p = client->property(propNames[i]); 0244 if (p) { 0245 client->properties.removeAll(p); 0246 SmFreeProperty(p); 0247 } 0248 } 0249 } 0250 0251 void KSMGetPropertiesProc(SmsConn smsConn, SmPointer managerData) 0252 { 0253 KSMClient *client = (KSMClient *)managerData; 0254 SmProp **props = new SmProp *[client->properties.count()]; 0255 int i = 0; 0256 foreach (SmProp *prop, client->properties) 0257 props[i++] = prop; 0258 0259 SmsReturnProperties(smsConn, i, props); 0260 delete[] props; 0261 } 0262 0263 class KSMListener : public QSocketNotifier 0264 { 0265 public: 0266 KSMListener(IceListenObj obj) 0267 : QSocketNotifier(IceGetListenConnectionNumber(obj), QSocketNotifier::Read) 0268 , listenObj(obj) 0269 { 0270 } 0271 0272 IceListenObj listenObj; 0273 }; 0274 0275 class KSMConnection : public QSocketNotifier 0276 { 0277 public: 0278 KSMConnection(IceConn conn) 0279 : QSocketNotifier(IceConnectionNumber(conn), QSocketNotifier::Read) 0280 , iceConn(conn) 0281 { 0282 } 0283 0284 IceConn iceConn; 0285 }; 0286 0287 /* for printing hex digits */ 0288 static void fprintfhex(FILE *fp, unsigned int len, char *cp) 0289 { 0290 static const char hexchars[] = "0123456789abcdef"; 0291 0292 for (; len > 0; len--, cp++) { 0293 unsigned char s = *cp; 0294 putc(hexchars[s >> 4], fp); 0295 putc(hexchars[s & 0x0f], fp); 0296 } 0297 } 0298 0299 /* 0300 * We use temporary files which contain commands to add/remove entries from 0301 * the .ICEauthority file. 0302 */ 0303 static void write_iceauth(FILE *addfp, FILE *removefp, IceAuthDataEntry *entry) 0304 { 0305 fprintf(addfp, "add %s \"\" %s %s ", entry->protocol_name, entry->network_id, entry->auth_name); 0306 fprintfhex(addfp, entry->auth_data_length, entry->auth_data); 0307 fprintf(addfp, "\n"); 0308 0309 fprintf(removefp, "remove protoname=%s protodata=\"\" netid=%s authname=%s\n", entry->protocol_name, entry->network_id, entry->auth_name); 0310 } 0311 0312 #define MAGIC_COOKIE_LEN 16 0313 0314 Status SetAuthentication_local(int count, IceListenObj *listenObjs) 0315 { 0316 int i; 0317 for (i = 0; i < count; i++) { 0318 char *prot = IceGetListenConnectionString(listenObjs[i]); 0319 if (!prot) 0320 continue; 0321 char *host = strchr(prot, '/'); 0322 char *sock = nullptr; 0323 if (host) { 0324 *host = 0; 0325 host++; 0326 sock = strchr(host, ':'); 0327 if (sock) { 0328 *sock = 0; 0329 sock++; 0330 } 0331 } 0332 qCDebug(KSMSERVER) << "KSMServer: SetAProc_loc: conn " << (unsigned)i << ", prot=" << prot << ", file=" << sock; 0333 if (sock && !strcmp(prot, "local")) { 0334 chmod(sock, 0700); 0335 } 0336 IceSetHostBasedAuthProc(listenObjs[i], HostBasedAuthProc); 0337 free(prot); 0338 } 0339 return 1; 0340 } 0341 0342 Status SetAuthentication(int count, IceListenObj *listenObjs, IceAuthDataEntry **authDataEntries) 0343 { 0344 QTemporaryFile addTempFile; 0345 remTempFile = new QTemporaryFile; 0346 0347 if (!addTempFile.open() || !remTempFile->open()) 0348 return 0; 0349 0350 if ((*authDataEntries = (IceAuthDataEntry *)malloc(count * 2 * sizeof(IceAuthDataEntry))) == nullptr) 0351 return 0; 0352 0353 FILE *addAuthFile = fopen(QFile::encodeName(addTempFile.fileName()).constData(), "r+"); 0354 FILE *remAuthFile = fopen(QFile::encodeName(remTempFile->fileName()).constData(), "r+"); 0355 0356 for (int i = 0; i < numTransports * 2; i += 2) { 0357 (*authDataEntries)[i].network_id = IceGetListenConnectionString(listenObjs[i / 2]); 0358 (*authDataEntries)[i].protocol_name = (char *)"ICE"; 0359 (*authDataEntries)[i].auth_name = (char *)"MIT-MAGIC-COOKIE-1"; 0360 0361 (*authDataEntries)[i].auth_data = IceGenerateMagicCookie(MAGIC_COOKIE_LEN); 0362 (*authDataEntries)[i].auth_data_length = MAGIC_COOKIE_LEN; 0363 0364 (*authDataEntries)[i + 1].network_id = IceGetListenConnectionString(listenObjs[i / 2]); 0365 (*authDataEntries)[i + 1].protocol_name = (char *)"XSMP"; 0366 (*authDataEntries)[i + 1].auth_name = (char *)"MIT-MAGIC-COOKIE-1"; 0367 0368 (*authDataEntries)[i + 1].auth_data = IceGenerateMagicCookie(MAGIC_COOKIE_LEN); 0369 (*authDataEntries)[i + 1].auth_data_length = MAGIC_COOKIE_LEN; 0370 0371 write_iceauth(addAuthFile, remAuthFile, &(*authDataEntries)[i]); 0372 write_iceauth(addAuthFile, remAuthFile, &(*authDataEntries)[i + 1]); 0373 0374 IceSetPaAuthData(2, &(*authDataEntries)[i]); 0375 0376 IceSetHostBasedAuthProc(listenObjs[i / 2], HostBasedAuthProc); 0377 } 0378 fclose(addAuthFile); 0379 fclose(remAuthFile); 0380 0381 QString iceAuth = QStandardPaths::findExecutable(QStringLiteral("iceauth")); 0382 if (iceAuth.isEmpty()) { 0383 qCWarning(KSMSERVER, "KSMServer: could not find iceauth"); 0384 return 0; 0385 } 0386 0387 KProcess p; 0388 p << iceAuth << QStringLiteral("source") << addTempFile.fileName(); 0389 p.execute(); 0390 0391 return (1); 0392 } 0393 0394 /* 0395 * Free up authentication data. 0396 */ 0397 void FreeAuthenticationData(int count, IceAuthDataEntry *authDataEntries) 0398 { 0399 /* Each transport has entries for ICE and XSMP */ 0400 if (only_local) 0401 return; 0402 0403 for (int i = 0; i < count * 2; i++) { 0404 free(authDataEntries[i].network_id); 0405 free(authDataEntries[i].auth_data); 0406 } 0407 0408 free(authDataEntries); 0409 0410 QString iceAuth = QStandardPaths::findExecutable(QStringLiteral("iceauth")); 0411 if (iceAuth.isEmpty()) { 0412 qCWarning(KSMSERVER, "KSMServer: could not find iceauth"); 0413 return; 0414 } 0415 0416 if (remTempFile) { 0417 KProcess p; 0418 p << iceAuth << QStringLiteral("source") << remTempFile->fileName(); 0419 p.execute(); 0420 } 0421 0422 delete remTempFile; 0423 remTempFile = nullptr; 0424 } 0425 0426 static int Xio_ErrorHandler(Display *) 0427 { 0428 qCWarning(KSMSERVER, "ksmserver: Fatal IO error: client killed"); 0429 0430 // Don't do anything that might require the X connection 0431 if (the_server) { 0432 KSMServer *server = the_server; 0433 the_server = nullptr; 0434 server->cleanUp(); 0435 // Don't delete server!! 0436 } 0437 0438 exit(0); // Don't report error, it's not our fault. 0439 return 0; // Bogus return value, notreached 0440 } 0441 0442 void KSMServer::setupXIOErrorHandler() 0443 { 0444 XSetIOErrorHandler(Xio_ErrorHandler); 0445 } 0446 0447 static int wake_up_socket = -1; 0448 static void sighandler(int sig) 0449 { 0450 if (sig == SIGHUP) { 0451 signal(SIGHUP, sighandler); 0452 return; 0453 } 0454 0455 char ch = 0; 0456 (void)::write(wake_up_socket, &ch, 1); 0457 } 0458 0459 void KSMWatchProc(IceConn iceConn, IcePointer client_data, Bool opening, IcePointer *watch_data) 0460 { 0461 KSMServer *ds = (KSMServer *)client_data; 0462 0463 if (opening) { 0464 *watch_data = (IcePointer)ds->watchConnection(iceConn); 0465 } else { 0466 ds->removeConnection((KSMConnection *)*watch_data); 0467 } 0468 } 0469 0470 static Status KSMNewClientProc(SmsConn conn, SmPointer manager_data, unsigned long *mask_ret, SmsCallbacks *cb, char **failure_reason_ret) 0471 { 0472 *failure_reason_ret = nullptr; 0473 0474 void *client = ((KSMServer *)manager_data)->newClient(conn); 0475 if (client == NULL) { 0476 const char *errstr = "Connection rejected: ksmserver is shutting down"; 0477 qCWarning(KSMSERVER, "%s", errstr); 0478 0479 if ((*failure_reason_ret = (char *)malloc(strlen(errstr) + 1)) != NULL) { 0480 strcpy(*failure_reason_ret, errstr); 0481 } 0482 return 0; 0483 } 0484 0485 cb->register_client.callback = KSMRegisterClientProc; 0486 cb->register_client.manager_data = client; 0487 cb->interact_request.callback = KSMInteractRequestProc; 0488 cb->interact_request.manager_data = client; 0489 cb->interact_done.callback = KSMInteractDoneProc; 0490 cb->interact_done.manager_data = client; 0491 cb->save_yourself_request.callback = KSMSaveYourselfRequestProc; 0492 cb->save_yourself_request.manager_data = client; 0493 cb->save_yourself_phase2_request.callback = KSMSaveYourselfPhase2RequestProc; 0494 cb->save_yourself_phase2_request.manager_data = client; 0495 cb->save_yourself_done.callback = KSMSaveYourselfDoneProc; 0496 cb->save_yourself_done.manager_data = client; 0497 cb->close_connection.callback = KSMCloseConnectionProc; 0498 cb->close_connection.manager_data = client; 0499 cb->set_properties.callback = KSMSetPropertiesProc; 0500 cb->set_properties.manager_data = client; 0501 cb->delete_properties.callback = KSMDeletePropertiesProc; 0502 cb->delete_properties.manager_data = client; 0503 cb->get_properties.callback = KSMGetPropertiesProc; 0504 cb->get_properties.manager_data = client; 0505 0506 *mask_ret = SmsRegisterClientProcMask | SmsInteractRequestProcMask | SmsInteractDoneProcMask | SmsSaveYourselfRequestProcMask 0507 | SmsSaveYourselfP2RequestProcMask | SmsSaveYourselfDoneProcMask | SmsCloseConnectionProcMask | SmsSetPropertiesProcMask | SmsDeletePropertiesProcMask 0508 | SmsGetPropertiesProcMask; 0509 return 1; 0510 } 0511 0512 #ifdef HAVE__ICETRANSNOLISTEN 0513 extern "C" int _IceTransNoListen(const char *protocol); 0514 #endif 0515 0516 KSMServer::KSMServer(InitFlags flags) 0517 : sessionGroup(QLatin1String("")) 0518 , m_kwinInterface(new OrgKdeKWinSessionInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Session"), QDBusConnection::sessionBus(), this)) 0519 , sockets{-1, -1} 0520 { 0521 if (!flags.testFlag(InitFlag::NoLockScreen)) { 0522 ScreenLocker::KSldApp::self()->initialize(); 0523 if (flags.testFlag(InitFlag::ImmediateLockScreen)) { 0524 ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate); 0525 } 0526 } 0527 0528 if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets) != 0) 0529 qFatal("Could not create socket pair, error %d (%s)", errno, strerror(errno)); 0530 wake_up_socket = sockets[0]; 0531 QSocketNotifier *n = new QSocketNotifier(sockets[1], QSocketNotifier::Read, this); 0532 qApp->connect(n, &QSocketNotifier::activated, &QApplication::quit); 0533 0534 new KSMServerInterfaceAdaptor(this); 0535 QDBusConnection::sessionBus().registerObject(QStringLiteral("/KSMServer"), this); 0536 the_server = this; 0537 clean = false; 0538 0539 state = Idle; 0540 saveSession = false; 0541 KConfigGroup config(KSharedConfig::openConfig(), "General"); 0542 clientInteracting = nullptr; 0543 xonCommand = config.readEntry("xonCommand", "xon"); 0544 0545 only_local = flags.testFlag(InitFlag::OnlyLocal); 0546 #ifdef HAVE__ICETRANSNOLISTEN 0547 if (only_local) 0548 _IceTransNoListen("tcp"); 0549 #else 0550 only_local = false; 0551 #endif 0552 0553 char errormsg[256]; 0554 if (!SmsInitialize((char *)KSMVendorString, (char *)KSMReleaseString, KSMNewClientProc, (SmPointer)this, HostBasedAuthProc, 256, errormsg)) { 0555 qCWarning(KSMSERVER, "KSMServer: could not register XSM protocol"); 0556 } 0557 0558 if (!IceListenForConnections(&numTransports, &listenObjs, 256, errormsg)) { 0559 qCWarning(KSMSERVER, "KSMServer: Error listening for connections: %s", errormsg); 0560 qCWarning(KSMSERVER, "KSMServer: Aborting."); 0561 exit(1); 0562 } 0563 0564 { 0565 // publish available transports. 0566 QByteArray fName = 0567 QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QDir::separator() + QStringLiteral("KSMserver")); 0568 qCDebug(KSMSERVER) << fName; 0569 QString display = QString::fromLocal8Bit(::getenv("DISPLAY")); 0570 // strip the screen number from the display 0571 display.remove(QRegularExpression(QStringLiteral("\\.[0-9]+$"))); 0572 int i; 0573 while ((i = display.indexOf(QLatin1Char(':'))) >= 0) 0574 display[i] = QLatin1Char('_'); 0575 while ((i = display.indexOf(QLatin1Char('/'))) >= 0) 0576 display[i] = QLatin1Char('_'); 0577 0578 fName += '_' + display.toLocal8Bit(); 0579 FILE *f; 0580 f = ::fopen(fName.data(), "w+"); 0581 if (!f) { 0582 qCWarning(KSMSERVER, "KSMServer: cannot open %s: %s", fName.data(), strerror(errno)); 0583 qCWarning(KSMSERVER, "KSMServer: Aborting."); 0584 exit(1); 0585 } 0586 char *session_manager = IceComposeNetworkIdList(numTransports, listenObjs); 0587 fprintf(f, "%s\n%i\n", session_manager, getpid()); 0588 fclose(f); 0589 setenv("SESSION_MANAGER", session_manager, true); 0590 0591 auto updateEnvJob = new UpdateLaunchEnvJob(QStringLiteral("SESSION_MANAGER"), QString::fromLatin1(session_manager)); 0592 updateEnvJob->exec(); 0593 0594 free(session_manager); 0595 } 0596 0597 if (only_local) { 0598 if (!SetAuthentication_local(numTransports, listenObjs)) 0599 qFatal("KSMSERVER: authentication setup failed."); 0600 } else { 0601 if (!SetAuthentication(numTransports, listenObjs, &authDataEntries)) 0602 qFatal("KSMSERVER: authentication setup failed."); 0603 } 0604 0605 IceAddConnectionWatch(KSMWatchProc, (IcePointer)this); 0606 0607 KSMListener *con; 0608 for (int i = 0; i < numTransports; i++) { 0609 fcntl(IceGetListenConnectionNumber(listenObjs[i]), F_SETFD, FD_CLOEXEC); 0610 con = new KSMListener(listenObjs[i]); 0611 listener.append(con); 0612 connect(con, &KSMListener::activated, this, &KSMServer::newConnection); 0613 } 0614 0615 signal(SIGHUP, sighandler); 0616 signal(SIGTERM, sighandler); 0617 signal(SIGINT, sighandler); 0618 signal(SIGPIPE, SIG_IGN); 0619 0620 connect(&protectionTimer, &QTimer::timeout, this, &KSMServer::protectionTimeout); 0621 connect(&restoreTimer, &QTimer::timeout, this, &KSMServer::tryRestoreNext); 0622 connect(this, &KSMServer::sessionRestored, this, [this]() { 0623 auto reply = m_restoreSessionCall.createReply(); 0624 QDBusConnection::sessionBus().send(reply); 0625 m_restoreSessionCall = QDBusMessage(); 0626 }); 0627 connect(qApp, &QApplication::aboutToQuit, this, &KSMServer::cleanUp); 0628 0629 setupXIOErrorHandler(); 0630 0631 QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), 0632 QStringLiteral("/KSplash"), 0633 QStringLiteral("org.kde.KSplash"), 0634 QStringLiteral("setStage")); 0635 ksplashProgressMessage.setArguments({QStringLiteral("ksmserver")}); 0636 QDBusConnection::sessionBus().call(ksplashProgressMessage, QDBus::NoBlock); 0637 } 0638 0639 KSMServer::~KSMServer() 0640 { 0641 qDeleteAll(listener); 0642 the_server = nullptr; 0643 cleanUp(); 0644 } 0645 0646 void KSMServer::cleanUp() 0647 { 0648 if (clean) 0649 return; 0650 clean = true; 0651 IceFreeListenObjs(numTransports, listenObjs); 0652 0653 wake_up_socket = -1; 0654 ::close(sockets[1]); 0655 ::close(sockets[0]); 0656 sockets[0] = -1; 0657 sockets[1] = -1; 0658 0659 QByteArray fName = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QLatin1Char('/') + QStringLiteral("KSMserver")); 0660 QString display = QString::fromLocal8Bit(::getenv("DISPLAY")); 0661 // strip the screen number from the display 0662 display.remove(QRegularExpression(QStringLiteral("\\.[0-9]+$"))); 0663 int i; 0664 while ((i = display.indexOf(QLatin1Char(':'))) >= 0) 0665 display[i] = QLatin1Char('_'); 0666 while ((i = display.indexOf(QLatin1Char('/'))) >= 0) 0667 display[i] = QLatin1Char('_'); 0668 0669 fName += '_' + display.toLocal8Bit(); 0670 ::unlink(fName.data()); 0671 0672 FreeAuthenticationData(numTransports, authDataEntries); 0673 signal(SIGTERM, SIG_DFL); 0674 signal(SIGINT, SIG_DFL); 0675 } 0676 0677 void *KSMServer::watchConnection(IceConn iceConn) 0678 { 0679 KSMConnection *conn = new KSMConnection(iceConn); 0680 connect(conn, &KSMConnection::activated, this, &KSMServer::processData); 0681 return (void *)conn; 0682 } 0683 0684 void KSMServer::removeConnection(KSMConnection *conn) 0685 { 0686 delete conn; 0687 } 0688 0689 /*! 0690 Called from our IceIoErrorHandler 0691 */ 0692 void KSMServer::ioError(IceConn /*iceConn*/) 0693 { 0694 } 0695 0696 void KSMServer::processData(int /*socket*/) 0697 { 0698 IceConn iceConn = ((KSMConnection *)sender())->iceConn; 0699 IceProcessMessagesStatus status = IceProcessMessages(iceConn, nullptr, nullptr); 0700 if (status == IceProcessMessagesIOError) { 0701 IceSetShutdownNegotiation(iceConn, false); 0702 QList<KSMClient *>::iterator it = clients.begin(); 0703 QList<KSMClient *>::iterator const itEnd = clients.end(); 0704 while ((it != itEnd) && *it && (SmsGetIceConnection((*it)->connection()) != iceConn)) 0705 ++it; 0706 if ((it != itEnd) && *it) { 0707 SmsConn smsConn = (*it)->connection(); 0708 deleteClient(*it); 0709 SmsCleanUp(smsConn); 0710 } 0711 (void)IceCloseConnection(iceConn); 0712 } 0713 } 0714 0715 KSMClient *KSMServer::newClient(SmsConn conn) 0716 { 0717 KSMClient *client = nullptr; 0718 if (state != Killing) { 0719 client = new KSMClient(conn); 0720 clients.append(client); 0721 } 0722 return client; 0723 } 0724 0725 void KSMServer::deleteClient(KSMClient *client) 0726 { 0727 if (!clients.contains(client)) // paranoia 0728 return; 0729 clients.removeAll(client); 0730 clientsToKill.removeAll(client); 0731 clientsToSave.removeAll(client); 0732 if (client == clientInteracting) { 0733 clientInteracting = nullptr; 0734 handlePendingInteractions(); 0735 } 0736 delete client; 0737 if (state == Shutdown || state == Checkpoint || state == ClosingSubSession) 0738 completeShutdownOrCheckpoint(); 0739 if (state == Killing) 0740 completeKilling(); 0741 else if (state == KillingSubSession) 0742 completeKillingSubSession(); 0743 } 0744 0745 void KSMServer::newConnection(int /*socket*/) 0746 { 0747 IceAcceptStatus status; 0748 IceConn iceConn = IceAcceptConnection(((KSMListener *)sender())->listenObj, &status); 0749 if (iceConn == nullptr) 0750 return; 0751 IceSetShutdownNegotiation(iceConn, false); 0752 IceConnectStatus cstatus; 0753 while ((cstatus = IceConnectionStatus(iceConn)) == IceConnectPending) { 0754 (void)IceProcessMessages(iceConn, nullptr, nullptr); 0755 } 0756 0757 if (cstatus != IceConnectAccepted) { 0758 if (cstatus == IceConnectIOError) 0759 qCDebug(KSMSERVER) << "IO error opening ICE Connection!"; 0760 else 0761 qCDebug(KSMSERVER) << "ICE Connection rejected!"; 0762 (void)IceCloseConnection(iceConn); 0763 return; 0764 } 0765 0766 // don't leak the fd 0767 fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC); 0768 } 0769 0770 QString KSMServer::currentSession() 0771 { 0772 if (sessionGroup.startsWith(QLatin1String("Session: "))) 0773 return sessionGroup.mid(9); 0774 return QLatin1String(""); // empty, not null, since used for KConfig::setGroup // TODO does this comment make any sense? 0775 } 0776 0777 void KSMServer::discardSession() 0778 { 0779 KConfigGroup config(KSharedConfig::openConfig(), sessionGroup); 0780 int count = config.readEntry("count", 0); 0781 foreach (KSMClient *c, clients) { 0782 QStringList discardCommand = c->discardCommand(); 0783 if (discardCommand.isEmpty()) 0784 continue; 0785 // check that non of the old clients used the exactly same 0786 // discardCommand before we execute it. This used to be the 0787 // case up to KDE and Qt < 3.1 0788 int i = 1; 0789 while (i <= count && config.readPathEntry(QStringLiteral("discardCommand") + QString::number(i), QStringList()) != discardCommand) 0790 i++; 0791 if (i <= count) 0792 executeCommand(discardCommand); 0793 } 0794 } 0795 0796 void KSMServer::storeSession() 0797 { 0798 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0799 config->reparseConfiguration(); // config may have changed in the KControl module 0800 KConfigGroup generalGroup(config, "General"); 0801 excludeApps = generalGroup.readEntry("excludeApps").toLower().split(QRegularExpression(QStringLiteral("[,:]")), Qt::SkipEmptyParts); 0802 KConfigGroup configSessionGroup(config, sessionGroup); 0803 int count = configSessionGroup.readEntry("count", 0); 0804 for (int i = 1; i <= count; i++) { 0805 QStringList discardCommand = configSessionGroup.readPathEntry(QLatin1String("discardCommand") + QString::number(i), QStringList()); 0806 if (discardCommand.isEmpty()) 0807 continue; 0808 // check that non of the new clients uses the exactly same 0809 // discardCommand before we execute it. This used to be the 0810 // case up to KDE and Qt < 3.1 0811 QList<KSMClient *>::iterator it = clients.begin(); 0812 QList<KSMClient *>::iterator const itEnd = clients.end(); 0813 while ((it != itEnd) && *it && (discardCommand != (*it)->discardCommand())) 0814 ++it; 0815 if ((it != itEnd) && *it) 0816 continue; 0817 executeCommand(discardCommand); 0818 } 0819 config->deleteGroup(sessionGroup); // ### does not work with global config object... 0820 KConfigGroup cg(config, sessionGroup); 0821 count = 0; 0822 0823 // Tell kwin to save its state 0824 auto reply = m_kwinInterface->finishSaveSession(currentSession()); 0825 reply.waitForFinished(); // boo! 0826 0827 foreach (KSMClient *c, clients) { 0828 int restartHint = c->restartStyleHint(); 0829 if (restartHint == SmRestartNever) 0830 continue; 0831 QString program = c->program(); 0832 QStringList restartCommand = c->restartCommand(); 0833 if (program.isEmpty() && restartCommand.isEmpty()) 0834 continue; 0835 if (state == ClosingSubSession && !clientsToSave.contains(c)) 0836 continue; 0837 0838 // 'program' might be (mostly) fullpath, or (sometimes) just the name. 0839 // 'name' is just the name. 0840 QFileInfo info(program); 0841 const QString &name = info.fileName(); 0842 0843 if (excludeApps.contains(program.toLower()) || excludeApps.contains(name.toLower())) { 0844 continue; 0845 } 0846 0847 count++; 0848 QString n = QString::number(count); 0849 cg.writeEntry(QStringLiteral("program") + n, program); 0850 cg.writeEntry(QStringLiteral("clientId") + n, c->clientId()); 0851 cg.writeEntry(QStringLiteral("restartCommand") + n, restartCommand); 0852 cg.writePathEntry(QStringLiteral("discardCommand") + n, c->discardCommand()); 0853 cg.writeEntry(QStringLiteral("restartStyleHint") + n, restartHint); 0854 cg.writeEntry(QStringLiteral("userId") + n, c->userId()); 0855 } 0856 cg.writeEntry("count", count); 0857 0858 KConfigGroup cg2(config, "General"); 0859 0860 storeLegacySession(config.data()); 0861 config->sync(); 0862 } 0863 0864 QStringList KSMServer::sessionList() 0865 { 0866 QStringList sessions(QStringLiteral("default")); 0867 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0868 const QStringList groups = config->groupList(); 0869 for (QStringList::ConstIterator it = groups.constBegin(); it != groups.constEnd(); ++it) 0870 if ((*it).startsWith(QLatin1String("Session: "))) 0871 sessions << (*it).mid(9); 0872 return sessions; 0873 } 0874 0875 bool KSMServer::defaultSession() const 0876 { 0877 return sessionGroup.isEmpty(); 0878 } 0879 0880 void KSMServer::setupShortcuts() 0881 { 0882 if (KAuthorized::authorize(QStringLiteral("logout"))) { 0883 KActionCollection *actionCollection = new KActionCollection(this); 0884 actionCollection->setComponentDisplayName(i18n("Session Management")); 0885 QAction *a; 0886 a = actionCollection->addAction(QStringLiteral("Log Out")); 0887 a->setText(i18n("Log Out")); 0888 KGlobalAccel::self()->setGlobalShortcut(a, QList<QKeySequence>() << (Qt::ALT | Qt::CTRL | Qt::Key_Delete)); 0889 connect(a, &QAction::triggered, this, &KSMServer::defaultLogout); 0890 0891 a = actionCollection->addAction(QStringLiteral("Log Out Without Confirmation")); 0892 a->setText(i18n("Log Out Without Confirmation")); 0893 KGlobalAccel::self()->setGlobalShortcut(a, QKeySequence()); 0894 connect(a, &QAction::triggered, this, &KSMServer::logoutWithoutConfirmation); 0895 0896 a = actionCollection->addAction(QStringLiteral("Halt Without Confirmation")); 0897 a->setText(i18n("Halt Without Confirmation")); 0898 KGlobalAccel::self()->setGlobalShortcut(a, QKeySequence()); 0899 connect(a, &QAction::triggered, this, &KSMServer::haltWithoutConfirmation); 0900 0901 a = actionCollection->addAction(QStringLiteral("Reboot Without Confirmation")); 0902 a->setText(i18n("Reboot Without Confirmation")); 0903 KGlobalAccel::self()->setGlobalShortcut(a, QKeySequence()); 0904 connect(a, &QAction::triggered, this, &KSMServer::rebootWithoutConfirmation); 0905 } 0906 } 0907 0908 void KSMServer::setRestoreSession(const QString &sessionName) 0909 { 0910 if (state != Idle) 0911 return; 0912 #ifdef KSMSERVER_STARTUP_DEBUG1 0913 t.start(); 0914 #endif 0915 0916 qCDebug(KSMSERVER) << "KSMServer::restoreSession " << sessionName; 0917 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0918 0919 sessionGroup = QLatin1String("Session: ") + sessionName; 0920 KConfigGroup configSessionGroup(config, sessionGroup); 0921 0922 int count = configSessionGroup.readEntry("count", 0); 0923 appsToStart = count; 0924 } 0925 0926 /*! 0927 Starts the default session. 0928 */ 0929 void KSMServer::startDefaultSession() 0930 { 0931 if (state != Idle) 0932 return; 0933 #ifdef KSMSERVER_STARTUP_DEBUG1 0934 t.start(); 0935 #endif 0936 sessionGroup = QString(); 0937 } 0938 0939 void KSMServer::restoreSession() 0940 { 0941 Q_ASSERT(calledFromDBus()); 0942 if (defaultSession()) { 0943 state = KSMServer::Idle; 0944 return; 0945 } 0946 0947 setDelayedReply(true); 0948 m_restoreSessionCall = message(); 0949 0950 lastAppStarted = 0; 0951 lastIdStarted.clear(); 0952 state = KSMServer::Restoring; 0953 0954 auto reply = m_kwinInterface->loadSession(currentSession()); 0955 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); 0956 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, reply](QDBusPendingCallWatcher *watcher) { 0957 watcher->deleteLater(); 0958 if (reply.isError()) { 0959 qWarning() << "Failed to notify kwin of current session " << reply.error().message(); 0960 } 0961 restoreLegacySession(KSharedConfig::openConfig().data()); 0962 tryRestoreNext(); 0963 }); 0964 } 0965 0966 void KSMServer::restoreSubSession(const QString &name) 0967 { 0968 sessionGroup = QStringLiteral("SubSession: ") + name; 0969 0970 KConfigGroup configSessionGroup(KSharedConfig::openConfig(), sessionGroup); 0971 int count = configSessionGroup.readEntry("count", 0); 0972 appsToStart = count; 0973 lastAppStarted = 0; 0974 lastIdStarted.clear(); 0975 0976 state = RestoringSubSession; 0977 tryRestoreNext(); 0978 } 0979 0980 void KSMServer::clientRegistered(const char *previousId) 0981 { 0982 if (previousId && lastIdStarted == QString::fromLocal8Bit(previousId)) 0983 tryRestoreNext(); 0984 } 0985 0986 void KSMServer::tryRestoreNext() 0987 { 0988 if (state != Restoring && state != RestoringSubSession) 0989 return; 0990 restoreTimer.stop(); 0991 KConfigGroup config(KSharedConfig::openConfig(), sessionGroup); 0992 0993 while (lastAppStarted < appsToStart) { 0994 lastAppStarted++; 0995 QString n = QString::number(lastAppStarted); 0996 QString clientId = config.readEntry(QLatin1String("clientId") + n, QString()); 0997 bool alreadyStarted = false; 0998 foreach (KSMClient *c, clients) { 0999 if (QString::fromLocal8Bit(c->clientId()) == clientId) { 1000 alreadyStarted = true; 1001 break; 1002 } 1003 } 1004 if (alreadyStarted) 1005 continue; 1006 1007 QStringList restartCommand = config.readEntry(QLatin1String("restartCommand") + n, QStringList()); 1008 if (restartCommand.isEmpty() || (config.readEntry(QStringLiteral("restartStyleHint") + n, 0) == SmRestartNever)) { 1009 continue; 1010 } 1011 startApplication(restartCommand, 1012 config.readEntry(QStringLiteral("clientMachine") + n, QString()), 1013 config.readEntry(QStringLiteral("userId") + n, QString())); 1014 lastIdStarted = clientId; 1015 if (!lastIdStarted.isEmpty()) { 1016 restoreTimer.setSingleShot(true); 1017 restoreTimer.start(2000); 1018 return; // we get called again from the clientRegistered handler 1019 } 1020 } 1021 1022 // all done 1023 appsToStart = 0; 1024 lastIdStarted.clear(); 1025 1026 if (state == Restoring) { 1027 Q_EMIT sessionRestored(); 1028 } else { // subsession 1029 Q_EMIT subSessionOpened(); 1030 } 1031 state = Idle; 1032 } 1033 1034 void KSMServer::startupDone() 1035 { 1036 state = Idle; 1037 } 1038 1039 void KSMServer::defaultLogout() 1040 { 1041 shutdown(KWorkSpace::ShutdownConfirmYes, KWorkSpace::ShutdownTypeDefault, KWorkSpace::ShutdownModeDefault); 1042 } 1043 1044 void KSMServer::logoutWithoutConfirmation() 1045 { 1046 shutdown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeNone, KWorkSpace::ShutdownModeDefault); 1047 } 1048 1049 void KSMServer::haltWithoutConfirmation() 1050 { 1051 shutdown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeHalt, KWorkSpace::ShutdownModeDefault); 1052 } 1053 1054 void KSMServer::rebootWithoutConfirmation() 1055 { 1056 shutdown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeReboot, KWorkSpace::ShutdownModeDefault); 1057 } 1058 1059 void KSMServer::openSwitchUserDialog() 1060 { 1061 // this method exists only for compatibility. Users should ideally call this directly 1062 OrgKdeScreensaverInterface iface(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus()); 1063 iface.SwitchUser(); 1064 }