File indexing completed on 2024-04-28 09:22:02

0001 /*
0002 SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org>
0003 SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
0004 
0005 SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 #include <KLocalizedString>
0008 
0009 #include <QCommandLineParser>
0010 #include <QDateTime>
0011 #include <QSessionManager>
0012 #include <QSurfaceFormat>
0013 
0014 #include <iostream>
0015 
0016 #include <signal.h>
0017 
0018 #include "greeterapp.h"
0019 
0020 #include <config-kscreenlocker.h>
0021 #include <kscreenlocker_greet_logging.h>
0022 
0023 #if HAVE_SYS_PRCTL_H
0024 #include <sys/prctl.h>
0025 #endif
0026 #if HAVE_SYS_PROCCTL_H
0027 #include <sys/procctl.h>
0028 #include <unistd.h>
0029 #endif
0030 
0031 #include <KSignalHandler>
0032 #include <LayerShellQt/Shell>
0033 
0034 static void signalHandler(int signum)
0035 {
0036     ScreenLocker::UnlockApp *instance = qobject_cast<ScreenLocker::UnlockApp *>(QCoreApplication::instance());
0037 
0038     if (!instance) {
0039         return;
0040     }
0041 
0042     switch (signum) {
0043     case SIGTERM:
0044         // exit gracefully to not leave behind screensaver processes (bug#224200)
0045         // return exit code 1 to indicate that a valid password was not entered,
0046         // to prevent circumventing the password input by sending a SIGTERM
0047 
0048         qCDebug(KSCREENLOCKER_GREET) << "Greeter received SIGTERM. Will exit with error.";
0049         instance->exit(1);
0050         break;
0051     case SIGUSR1:
0052         qCDebug(KSCREENLOCKER_GREET) << "Greeter received SIGUSR1. Will lock immediately.";
0053         instance->lockImmediately();
0054         break;
0055     }
0056 }
0057 
0058 int main(int argc, char *argv[])
0059 {
0060     LayerShellQt::Shell::useLayerShell();
0061 
0062     // disable ptrace on the greeter
0063 #if HAVE_PR_SET_DUMPABLE
0064     prctl(PR_SET_DUMPABLE, 0);
0065 #endif
0066 #if HAVE_PROC_TRACE_CTL
0067     int mode = PROC_TRACE_CTL_DISABLE;
0068     procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode);
0069 #endif
0070 
0071     qCDebug(KSCREENLOCKER_GREET) << "Greeter is starting up.";
0072 
0073     KLocalizedString::setApplicationDomain(QByteArrayLiteral("kscreenlocker_greet"));
0074 
0075     // explicitly disable input methods on x11 as it makes it impossible to unlock, see BUG 306932
0076     // but explicitly set on screen keyboard such as maliit is allowed
0077     // on wayland, let the compositor take care of the input method
0078     if (!qEnvironmentVariableIsSet("WAYLAND_DISPLAY") && !qEnvironmentVariableIsSet("WAYLAND_SOCKET")
0079         && qgetenv("QT_IM_MODULE") != QByteArrayLiteral("maliit")) {
0080         qputenv("QT_IM_MODULE", QByteArrayLiteral("qtvirtualkeyboard"));
0081     }
0082 
0083     // Suppresses modal warnings about unwritable configuration files which may render the system inaccessible
0084     qputenv("KDE_HOME_READONLY", "1");
0085     // Kwin will re-lock if it restarts, reconnecting would leave us with two greeters but only one functional
0086     qunsetenv("QT_WAYLAND_RECONNECT");
0087     // Disable QML caching to prevent cache corruption in full or near-full disk scenarios.
0088     // https://bugs.kde.org/show_bug.cgi?id=471952
0089     // https://bugreports.qt.io/browse/QTBUG-117130
0090     qputenv("QML_DISABLE_DISK_CACHE", "1");
0091 
0092     auto format = QSurfaceFormat::defaultFormat();
0093     format.setOption(QSurfaceFormat::ResetNotification);
0094     QSurfaceFormat::setDefaultFormat(format);
0095 
0096     ScreenLocker::UnlockApp app(argc, argv);
0097 
0098     KSignalHandler::self()->watchSignal(SIGTERM);
0099     KSignalHandler::self()->watchSignal(SIGUSR1);
0100 
0101     // only connect signal handler once we can actual handle the signal properly
0102     QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, &app, &signalHandler);
0103 
0104     app.setQuitOnLastWindowClosed(false);
0105     app.setQuitLockEnabled(false);
0106     QCoreApplication::setApplicationName(QStringLiteral("kscreenlocker_greet"));
0107     QCoreApplication::setApplicationVersion(QStringLiteral("0.1"));
0108     QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
0109 
0110     // disable session management for the greeter
0111     auto disableSessionManagement = [](QSessionManager &sm) {
0112         sm.setRestartHint(QSessionManager::RestartNever);
0113     };
0114     QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement);
0115     QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement);
0116 
0117     QCommandLineParser parser;
0118     parser.setApplicationDescription(i18n("Greeter for the KDE Plasma Workspaces Screen locker"));
0119     parser.addHelpOption();
0120     parser.addVersionOption();
0121 
0122     QCommandLineOption testingOption(QStringLiteral("testing"), i18n("Starts the greeter in testing mode"));
0123 
0124     QCommandLineOption themeOption(QStringLiteral("theme"),
0125                                    i18n("Starts the greeter with the selected theme (only in Testing mode)"),
0126                                    QStringLiteral("theme"),
0127                                    QStringLiteral(""));
0128 
0129     QCommandLineOption immediateLockOption(QStringLiteral("immediateLock"), i18n("Lock immediately, ignoring any grace time etc."));
0130     QCommandLineOption graceTimeOption(QStringLiteral("graceTime"),
0131                                        i18n("Delay till the lock user interface gets shown in milliseconds."),
0132                                        QStringLiteral("milliseconds"),
0133                                        QStringLiteral("0"));
0134     QCommandLineOption nolockOption(QStringLiteral("nolock"), i18n("Don't show any lock user interface."));
0135     QCommandLineOption switchUserOption(QStringLiteral("switchuser"), i18n("Default to the switch user UI."));
0136 
0137     QCommandLineOption waylandFdOption(QStringLiteral("ksldfd"), i18n("File descriptor for connecting to ksld."), QStringLiteral("fd"));
0138 
0139     parser.addOption(testingOption);
0140     parser.addOption(themeOption);
0141     parser.addOption(immediateLockOption);
0142     parser.addOption(graceTimeOption);
0143     parser.addOption(nolockOption);
0144     parser.addOption(switchUserOption);
0145     parser.addOption(waylandFdOption);
0146     parser.process(app);
0147 
0148     if (parser.isSet(testingOption)) {
0149         app.setTesting(true);
0150         app.setImmediateLock(true);
0151 
0152         // parse theme option
0153         const QString theme = parser.value(themeOption);
0154         if (!theme.isEmpty()) {
0155             app.setTheme(theme);
0156         }
0157 
0158         // allow ptrace if testing is enabled
0159 #if HAVE_PR_SET_DUMPABLE
0160         prctl(PR_SET_DUMPABLE, 1);
0161 #endif
0162 #if HAVE_PROC_TRACE_CTL
0163         int mode = PROC_TRACE_CTL_ENABLE;
0164         procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode);
0165 #endif
0166     } else {
0167         app.setImmediateLock(parser.isSet(immediateLockOption));
0168     }
0169     app.setNoLock(parser.isSet(nolockOption));
0170 
0171     bool ok = false;
0172     int graceTime = parser.value(graceTimeOption).toInt(&ok);
0173     if (ok) {
0174         app.setGraceTime(graceTime);
0175     }
0176 
0177     if (parser.isSet(switchUserOption)) {
0178         app.setDefaultToSwitchUser(true);
0179     }
0180 
0181     if (parser.isSet(waylandFdOption)) {
0182         ok = false;
0183         const int fd = parser.value(waylandFdOption).toInt(&ok);
0184         if (ok) {
0185             app.setKsldSocket(fd);
0186         }
0187     }
0188 
0189     app.initialViewSetup();
0190 
0191     // This allow ksmserver to know when the application has actually finished setting itself up.
0192     // Crucial for blocking until it is ready, ensuring locking happens before sleep, e.g.
0193     std::cout << "Locked at " << QDateTime::currentDateTime().toSecsSinceEpoch() << std::endl;
0194 
0195     return app.exec();
0196 }