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 }