File indexing completed on 2024-04-28 05:35:30

0001 /*
0002     ksmserver - the KDE session management server
0003 
0004     SPDX-FileCopyrightText: 2000 Matthias Ettrich <ettrich@kde.org>
0005 
0006     SPDX-License-Identifier: MIT
0007 */
0008 
0009 #include <cerrno>
0010 #include <config-ksmserver.h>
0011 #include <config-workspace.h>
0012 #include <cstdlib>
0013 #include <cstring>
0014 #include <fcntl.h>
0015 #include <fixx11h.h>
0016 #include <unistd.h>
0017 
0018 #include "server.h"
0019 #include <KLocalizedString>
0020 #include <KMessageBox>
0021 #include <KRuntimePlatform>
0022 #include <KSelectionOwner>
0023 #include <KSharedConfig>
0024 #include <kconfig.h>
0025 #include <kconfiggroup.h>
0026 #include <kdbusservice.h>
0027 #include <ksmserver_debug.h>
0028 #include <kwindowsystem.h>
0029 #include <private/qtx11extras_p.h>
0030 
0031 #include <QApplication>
0032 #include <QCommandLineParser>
0033 #include <QFile>
0034 
0035 static const char version[] = "0.4";
0036 
0037 void IoErrorHandler(IceConn iceConn)
0038 {
0039     KSMServer::self()->ioError(iceConn);
0040 }
0041 
0042 bool writeTest(QByteArray path)
0043 {
0044     path += "/XXXXXX";
0045     int fd = mkstemp(path.data());
0046     if (fd == -1) {
0047         return false;
0048     }
0049     if (write(fd, "Hello World\n", 12) == -1) {
0050         int save_errno = errno;
0051         close(fd);
0052         unlink(path.data());
0053         errno = save_errno;
0054         return false;
0055     }
0056     close(fd);
0057     unlink(path.data());
0058     return true;
0059 }
0060 
0061 void sanity_check(int argc, char *argv[])
0062 {
0063     QString msg;
0064     QByteArray path = qgetenv("HOME");
0065     const QByteArray readOnly = qgetenv("KDE_HOME_READONLY");
0066     if (path.isEmpty()) {
0067         msg = i18n("$HOME not set!");
0068     }
0069     if (msg.isEmpty() && access(path.data(), W_OK)) {
0070         if (errno == ENOENT) {
0071             msg = i18n("$HOME directory (%1) does not exist.", QFile::decodeName(path));
0072         } else if (readOnly.isEmpty()) {
0073             msg = xi18nc("@info",
0074                          "No write access to $HOME directory (%1). If this is intentional, set <envar>KDE_HOME_READONLY=1</envar> in your environment.",
0075                          QFile::decodeName(path));
0076         }
0077     }
0078     if (msg.isEmpty() && access(path.data(), R_OK)) {
0079         if (errno == ENOENT) {
0080             msg = i18n("$HOME directory (%1) does not exist.", QFile::decodeName(path));
0081         } else {
0082             msg = i18n("No read access to $HOME directory (%1).", QFile::decodeName(path));
0083         }
0084     }
0085     if (msg.isEmpty() && readOnly.isEmpty() && !writeTest(path)) {
0086         if (errno == ENOSPC) {
0087             msg = i18n("$HOME directory (%1) is out of disk space.", QFile::decodeName(path));
0088         } else {
0089             msg = i18n(
0090                 "Writing to the $HOME directory (%2) failed with "
0091                 "the error '%1'",
0092                 QString::fromLocal8Bit(strerror(errno)),
0093                 QFile::decodeName(path));
0094         }
0095     }
0096     if (msg.isEmpty()) {
0097         path = getenv("ICEAUTHORITY");
0098         if (path.isEmpty()) {
0099             path = qgetenv("HOME");
0100             path += "/.ICEauthority";
0101         }
0102 
0103         if (access(path.data(), W_OK) && (errno != ENOENT)) {
0104             msg = i18n("No write access to '%1'.", QFile::decodeName(path));
0105         } else if (access(path.data(), R_OK) && (errno != ENOENT)) {
0106             msg = i18n("No read access to '%1'.", QFile::decodeName(path));
0107         }
0108     }
0109     if (msg.isEmpty()) {
0110         path = getenv("KDETMP");
0111         if (path.isEmpty()) {
0112             path = "/tmp";
0113         }
0114         if (!writeTest(path)) {
0115             if (errno == ENOSPC) {
0116                 msg = i18n("Temp directory (%1) is out of disk space.", QFile::decodeName(path));
0117             } else {
0118                 msg = i18n(
0119                     "Writing to the temp directory (%2) failed with\n    "
0120                     "the error '%1'",
0121                     QString::fromLocal8Bit(strerror(errno)),
0122                     QFile::decodeName(path));
0123             }
0124         }
0125     }
0126     if (msg.isEmpty() && (path != "/tmp")) {
0127         path = "/tmp";
0128         if (!writeTest(path)) {
0129             if (errno == ENOSPC) {
0130                 msg = i18n("Temp directory (%1) is out of disk space.", QFile::decodeName(path));
0131             } else {
0132                 msg = i18n(
0133                     "Writing to the temp directory (%2) failed with\n    "
0134                     "the error '%1'",
0135                     QString::fromLocal8Bit(strerror(errno)),
0136                     QFile::decodeName(path));
0137             }
0138         }
0139     }
0140     if (msg.isEmpty()) {
0141         path += "/.ICE-unix";
0142         if (access(path.data(), W_OK) && (errno != ENOENT)) {
0143             msg = i18n("No write access to '%1'.", QFile::decodeName(path));
0144         } else if (access(path.data(), R_OK) && (errno != ENOENT)) {
0145             msg = i18n("No read access to '%1'.", QFile::decodeName(path));
0146         }
0147     }
0148     if (!msg.isEmpty()) {
0149         const QString msg_pre = i18n(
0150                                     "The following installation problem was detected\n"
0151                                     "while trying to start Plasma:")
0152             + QStringLiteral("\n\n    ");
0153         const QString msg_post = i18n("\n\nPlasma is unable to start.\n");
0154         fputs(msg_pre.toUtf8().constData(), stderr);
0155         fprintf(stderr, "%s", msg.toUtf8().constData());
0156         fputs(msg_post.toUtf8().constData(), stderr);
0157 
0158         QApplication a(argc, argv);
0159         const QString qmsg = msg_pre + msg + msg_post;
0160         KMessageBox::error(nullptr, qmsg, i18n("Plasma Workspace installation problem!"));
0161         exit(255);
0162     }
0163 }
0164 
0165 int main(int argc, char *argv[])
0166 {
0167     sanity_check(argc, argv);
0168 
0169     qunsetenv("SESSION_MANAGER");
0170 
0171     // force xcb QPA plugin as ksmserver is very X11 specific
0172     const QByteArray origQpaPlatform = qgetenv("QT_QPA_PLATFORM");
0173     qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb"));
0174 
0175     QCoreApplication::setQuitLockEnabled(false);
0176     auto a = new QGuiApplication(argc, argv);
0177 
0178     // now the QPA platform is set, unset variable again to not launch apps with incorrect environment
0179     if (origQpaPlatform.isEmpty()) {
0180         qunsetenv("QT_QPA_PLATFORM");
0181     } else {
0182         qputenv("QT_QPA_PLATFORM", origQpaPlatform);
0183     }
0184 
0185     QCoreApplication::setApplicationName(QStringLiteral("ksmserver"));
0186     QCoreApplication::setApplicationVersion(QString::fromLatin1(version));
0187     QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
0188 
0189     fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, 1);
0190 
0191     a->setQuitOnLastWindowClosed(false); // #169486
0192 
0193     QCommandLineParser parser;
0194     parser.setApplicationDescription(i18n("The reliable Plasma session manager that talks the standard X11R6 \nsession management protocol (XSMP)."));
0195     parser.addHelpOption();
0196     parser.addVersionOption();
0197 
0198     QCommandLineOption restoreOption(QStringList() << QStringLiteral("r") << QStringLiteral("restore"), i18n("Restores the saved user session if available"));
0199     parser.addOption(restoreOption);
0200 
0201     QCommandLineOption nolocalOption(QStringLiteral("nolocal"), i18n("Also allow remote connections"));
0202     parser.addOption(nolocalOption);
0203 
0204     QCommandLineOption lockscreenOption(QStringLiteral("lockscreen"), i18n("Starts the session in locked mode"));
0205     parser.addOption(lockscreenOption);
0206 
0207     QCommandLineOption noLockscreenOption(QStringLiteral("no-lockscreen"),
0208                                           i18n("Starts without lock screen support. Only needed if other component provides the lock screen."));
0209     parser.addOption(noLockscreenOption);
0210 
0211     parser.process(*a);
0212 
0213     bool only_local = !parser.isSet(nolocalOption);
0214 #ifndef HAVE__ICETRANSNOLISTEN
0215     /* this seems strange, but the default is only_local, so if !only_local
0216      * the option --nolocal was given, and we warn (the option --nolocal
0217      * does nothing on this platform, as here the default is reversed)
0218      */
0219     if (!only_local) {
0220         qCWarning(KSMSERVER, "--nolocal is not supported on your platform. Sorry.");
0221     }
0222     only_local = false;
0223 #endif
0224 
0225     KSMServer::InitFlags flags = KSMServer::InitFlag::None;
0226     if (only_local) {
0227         flags |= KSMServer::InitFlag::OnlyLocal;
0228     }
0229     if (parser.isSet(lockscreenOption)) {
0230         flags |= KSMServer::InitFlag::ImmediateLockScreen;
0231     }
0232     if (parser.isSet(noLockscreenOption)) {
0233         flags |= KSMServer::InitFlag::NoLockScreen;
0234     }
0235 
0236     // we use the session_type here as ksmserver is already forced as X above
0237     // in wayland, kwin manages the lock screen
0238     if (qgetenv("XDG_SESSION_TYPE") == QByteArrayLiteral("wayland")) {
0239         flags |= KSMServer::InitFlag::NoLockScreen;
0240     }
0241 
0242     auto server = new KSMServer(flags);
0243 
0244     // for the KDE-already-running check in startkde
0245     KSelectionOwner kde_running("_KDE_RUNNING", 0);
0246     kde_running.claim(false);
0247 
0248     IceSetIOErrorHandler(IoErrorHandler);
0249 
0250     KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("General"));
0251 
0252     QString loginMode = config.readEntry("loginMode", "restorePreviousLogout");
0253 
0254     // we don't need session restoring in Plasma Mobile
0255     if (KRuntimePlatform::runtimePlatform().contains(QStringLiteral("phone"))) {
0256         loginMode = QStringLiteral("emptySession");
0257     }
0258 
0259     if (parser.isSet(restoreOption)) {
0260         server->setRestoreSession(SESSION_BY_USER);
0261     } else if (loginMode == QLatin1String("restorePreviousLogout")) {
0262         server->setRestoreSession(SESSION_PREVIOUS_LOGOUT);
0263     } else if (loginMode == QLatin1String("restoreSavedSession")) {
0264         server->setRestoreSession(SESSION_BY_USER);
0265     } else {
0266         server->startDefaultSession();
0267     }
0268 
0269     KDBusService service(KDBusService::Unique);
0270 
0271     int ret = a->exec();
0272     kde_running.release(); // needs to be done before QGuiApplication destruction
0273     delete a;
0274     return ret;
0275 }